Mange skal bli flere (eller noe sånt)

I artikkelen Filtrer, Projiser, Aggreger presenterte jeg tre av de mest grunnleggende høyereordens-funksjonene man bruker til å behandle lister og andre datastrukturer. Grafisk kan de tre funksjonene illustreres slik:

filter_map_fold

Filter returnerer et subsett av den orginale strukturen. Map omformer strukturen, og returnerer en ny struktur som har et 1-til-1 forhold til de orginale dataene. Fold returnerer én verdi som er generert ved å analysere hele datastrukturen.

I denne blogposten vil jeg se nærmere på en fjærde funksjon, som kan illustreres på denne måten:

select_many

Denne funksjonen omformer/projiserer den orginale datastrukturen, på samme måte som map, men uten at det er et 1-til-1 forhold mellom input og output. Dette kan brukes til å “hente ut” data fra en mer kompleks datastruktur, men uten at det slås sammen slik som i fold/aggregate.

La oss anta at vi har en liste med SMS-meldinger. Hver melding har en meldingstekst, og vi er interessert i å lage en liste med alle ordene som inngår i meldingene. Dette tilfellet er ikke så veldig komplekst, men nok til å illustrere poenget. I C# kunne vi for eksempel ha opprettet en tom liste, og loopet over alle meldingene for så å trekke ut ordene fra teksten, slik som dette:

// ALTERNATIV 1 : foreach
var words1 = new List<string>();
foreach (var message in messages)
    words1.AddRange(message.Text.Split());

Takket være AddRange-metoden slipper vi en eksplisit indre loop for å legge til alle ordene.

Bruker du CodeRush i Visual Studio vil den foreslå at du omformer den vanlige foreach-loopen til en Linq ForEach action, slik som i alternativ 2:

// ALTERNATIV 2 : ForEach action
var words2 = new List<string>();
messages.ForEach(message => words2.AddRange(message.Text.Split()));

.. men i bunn og grunn er dette selvsagt akkurat det samme.

Kan vi bruke en høyereordens-funskjon i stedet? Ja, det er klart, vi kan for eksempel bruke Aggregate. Som du så lengre oppe skal aggregate returnere én verdi, men en liste er jo også én verdi, så det burde gå greit:

// ALTERNATIV 3 : Aggregate action
var words3 = messages.Aggregate(new List<string>(), (list, message) => 
{
    list.AddRange(message.Text.Split());
    return list;
});

I stedet for å opprette listen som en egen statement (som i alternativ 1 og 2) oppretter vi den her som første argument til aggregate. Den listen brukes så videre når en og en melding blir behandlet, og lambda-uttrykket må returnere listen for hver melding. Ja, dette fungerte, men dette ble ikke særlig elegant…

Nå er det derimot på tide å introdusere denne artikkelens hovedperson, funksjonen som i .NET har fått navnet SelectMany. Den gjør akkurat det vi er ute etter; vi spesifiserer en funksjon som gitt en melding returnerer en ny liste. SelectMany vil så slå sammen alle listene som produseres:

// ALTERNATIV 4 : SelectMany
IEnumerable<string> words4 = messages.SelectMany(message 
    => message.Text.Split());

Dette var klart mye bedre enn enn å bruke aggregate, og mer konsist og deklerativt enn å bruke foreach.

Med litt av det syntaktisk sukker som ligger i C# 3.0 og Linq kan dette også skrives slik:

// ALTERNATIV 5 : From x 2
var words5 = from message in messages
             from words in message.Text.Split()
             select words;

Slik jeg har forstått det genererer denne bruken av 2 from den samme koden som alternativ 4.

PS: F# har akkurat den samme funksjonen, men der heter den collect. Dette må ikke blandes med Ruby, hvor collect forvirrende nok er et alias for map. Jeg kjenner ikke til hvilke andre spåk som har funksjonen, og følgelig ikke hva de heter (men jeg er sikker på at Ameth vil fortelle meg alt om hvilke muligheter man har i Haskell).

Knagger: , , , ,

Kategorier: Polyglot.
RSS feed for kommentarene. Tilbaketråkk.

4 kommentarer til “Mange skal bli flere (eller noe sånt)”

  1. BjartN Says:

    Gleder meg allerede til neste post der (jeg antar at) du skal klart og tydelig forklare Computation Expressions/Monads i F# :)

  2. Torbjørn Says:

    Ask, and Ye Shall Receive! Antar jeg. Har ikke komt så langt i boka enda :D

  3. Ameth Says:

    Jasså, har jeg blitt forutsigbar nå? Du får få litt x86_64-assembly i stedet: http://asm.pastebin.com/S699HcVN

    (OK da, i Haskell: `concatMap words messages` eller, siden en liste er en monade, `messages >>= words`. (og jeg gleder meg og til du tar for deg monader ^_^))

  4. Torbjørn Says:

    Note to self: I Clojure heter denne funksjonen mapcat. Følgende kode gir en samplet liste med alle ordene:

    (mapcat #(.split % " ")
            '("This is a test"
              "And so is this"))
    

Skriv en kommentar

Tillatte tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Torbjørn: La oss anta to ulike definisjoner av Template Method pattern - de to ytterpunkte...

Lars-Petter: Hei igjen. Siden du inviterer til å ta diskusjonen i bloggen, og har tatt deg t...

Torbjørn: Lars-Petter: Det er én måte å se det på. Det er absolutt fortsatt Template M...

Lars-Petter: Hei. Har du ikke i prinsippet her gått over fra Template Method (arv) til Strat...

Christian Abildsø: I alle fall i C#, så føles dette som kode som blir mer fleksibel men vanskelig...

Torbjørn: Hei Henrik, og takk for tilbudet. Ble oppmerksom på Rasberry Pi for under en uk...

Henrik Sandaker Palm: Ang. større hobby prosjekt. Du er som er en slik rakker på programmering har j...

Øivind Nilsen: Slutt å bruke mobilnummeret mitt som eksempel !...

Bjørn Einar Bjartnes: Jeg har også latt meg fascinere av Clojure, uten at jeg har kommet så veldig l...

Bjørn Einar Bjartnes: Sweet :) Jeg tror egentlig jeg liker det som det er, med musikk. Litt av utford...

Mulig relaterte linker

 Hold deg oppdatert

Søk i bloggen

Ferske innlegg

  • Template Method del 4: Multippel arv
  • Template Method Intermesso
  • Template Method del 3: Bare funksjoner
  • Template Method del 2: På vei mot funksjonell programmering
  • Kategorier

  • .net ninja (37)
  • Bøker (17)
  • Diverse prosjekter (35)
  • DSL (10)
  • Erlang (10)
  • F# (5)
  • Hardware (1)
  • Jobb (77)
  • Julekalender (51)
  • kjempekjekt.com (23)
  • LISP/Clojure (33)
  • NNUG / community (60)
  • O/RM & databaser (10)
  • Off topic (116)
  • OO-design/clean code (30)
  • Podcasts (14)
  • Polyglot (77)
  • Ruby (27)
  • Silverlight / RIA (3)
  • Software/verktøy (20)
  • Softwareutvikling (20)
  • Testing / TDD (30)
  • the contiki strip (13)
  • User experience (3)
  • WCF (3)
  • Webutvikling (32)
  • WPF (9)
  • WTF (12)
  • Last ned Wallpaper

    Programmeringsbloggens tøffe skrivebordsbakgrunn med snippets fra ulike språk laster du ned her!

    Abonner via epost

    Om du vil kan du få alle nye blogposter tilsendt til din epost. Abonner nå, det er kjempeenkelt!

    Meta