3.3. Lister, lister og atter lister

Du er no ferdig med variablar og funksjonar, og er klar for det skumle området av Scheme som handlar om lister.

3.3.1. Å definere ei liste

Før vi ser meir på dette med lister, er det viktig at du kjenner skilnaden på delverdiar (av og til i programmerarverda kalla «atomverdiar») og lister.

Ein delverdi («atomic» verdi) er ein enkeltverdi. Vi brukte delverdiar då vi definerte variablar i den førre leksjonen og tileigna desse ein verdi. Som eit eksempel kan vi tileigna variabelen «x» delverdien 8 i dette uttrykket:

(let* ( (x 8) ) x)

(Her er variabelen x lagt til på slutten av uttrykket berre for å få skriva ut verdien tilordna x. Elles er dette ikkje normal skrivemåte men er gjort fordi let* arbeidar akkurat som ein funksjon og returnerer verdien i det siste uttrykket).

Ein variabel kan også referere til ei liste med verdiar i staden for berre til ein enkelt verdi. For å tileigna verdilista 1, 3, 5 til variabelen x skriv vi:

(let* ( (x '(1 3 5))) x)

Prøv å skriva inn begge uttrykka i Script-Fu konsollen og legg merke til svaret du får. Når du skriv den første setninga, vert svaret:

8

Den andre setning vil gje svaret:

(1 3 5)

Svaret «8» fortel at x inneheld «atomverdien» 8. Svaret (1 3 5) viser at x ikkje inneheld ein enkelt verdi, men ei liste med verdiar. Legg merke til at Scheme ikkje bruker komma mellom verdiane, verken i den lista du skriv inn eller i det svaret du får ut att.

For å definera ei liste, brukar du syntaksen:

'(a b c)

der a, b og c er verdiane. Apostrofen (') vert nytta for å fortelje at det som kjem etter inne i parentesen er ei liste med verkelege verdiar, ikkje funksjonar eller uttrykk.

Du kan også definere ei tom liste:

'()

eller berre:

()

Ei liste kan innehalda delverdiar eller nye lister:

(let*
   (
        (x
           '("GIMP" (1 2 3) ("er" ("topp" () ) ) )
        )
    )
    x
)
      

Legg merke til at det er nok med den første apostrofen. Prøv programmet i konsollen og sjå kva som kjem ut av det.

Du bør også merkja deg at det resultatet som vert returnert ikkje er ei liste med enkeltverdiar, men ei liste med bokstavlege verdiar. ("GIMP"), lista (1 2 3) osv.

3.3.2. Tenkt oppbygging av lister

Ofte kan det vera greit å sjå på listene som om dei er sett saman av eit «hovud» og ein «hale». Hovudet er det første elementet i lista, medan halen er resten av lista. Verkar kanskje noko merkeleg, men som du vil sjå seinare, er dette ikkje noko dum tenkemåte.

3.3.3. Å laga lister ved samankjeding (funksjonen Cons)

Ein av dei mest brukte innebygde funksjonane er funksjonen cons. Denne tek ein verdi og kjedar han saman med verdien frå det andre elementet, som er ei liste, og lagar ei ny liste. Frå det som vart sagt tidlegare, kan du sjå på ei liste som sett saman av eit element (hovudet) og resten av lista (halen). Det er også slik cons gjer det. Funksjonen legg eit element til hovudet på lista. Du kan altså laga ei liste slik:

(cons 1 '(2 3 4) )

Resultatet er den nye lista (1 2 3 4).

Du kan også laga ei liste med berre eitt element:

(cons 1 () )

Sjølvsagt kan du nytta tidlegare definerte variablar i listene i staden for direkteverdiane.

3.3.4. Å laga lister med funksjonen list

For å definera ei liste med ei blanding av direkteverdiar og tidlegare deklarerte variablar, nyttar du funksjonen list:

(list 5 4 3 a b c)

Dette vil laga og returnera ei liste med ei blanding av direkteverdiane «5», «4» og «3», og verdiane som er tileigna variablane a, b og c. Vi prøver med:

        (let*  (
                  (a 1)
                  (b 2)
                  (c 3)
               )

               (list 5 4 3 a b c)
        )
      

Dette vil resultera i lista (5 4 3 1 2 3).

3.3.5. Tilgang til verdiane i ei liste

Får å få tilgang til verdiane i ei liste bruker vi funksjonane car og cdr. car returnerar verdien av det første elementet i lista, hovudet, medan cdr returnerar resten av lista, halen. Som nemnd tidlegare kan ein sjå på ei liste som sett saman av hovud og hale.

3.3.6. Funksjonen car

car returnerar det første elementet i ei liste, altså listehovudet. Lista kan ikkje vera null (tom). Følgjande kommando vil altså returnera det første elementet i lista:

(car '("første" 2 "tredje"))

vil gje tilbake:

"første"

3.3.7. Funksjonen cdr

cdr returnerar resten av lista, etter det første elementet, dvs. halen. Dersom det berre er eitt element i lista, vert returverdien ei tom liste.

(cdr '("første" 2 "tredje"))

returnerar:

(2 "tredje")

medan det følgjande:

(cdr '("berre denne eine"))

returnerar:

()

3.3.8. Tilgang til andre element i ei liste

Vi kan altså nokså enkelt plukka ut listehovudet og listehalen, men kva om du ønskjer å ta ut verdien av det tredje elementet i ei liste? Det er fullt mogleg, men Scheme har ein noko merkeleg, og tungvindt, måte å gjera det på i høve til andre språk. Du må rett og slett plukka ut hovudet til halen passeleg mange gonger til du kjem fram til det elementet du ønskjer. Altså for eksempel hovudet til hovudet for listehalen (caadr) eller halen til halen i ei liste (cddr).

Den grunnleggande namnesetjinga er enklare. «a» står for hovudet og «d» for halen til lista, så

(car (cdr (car x) ) )

kan skrivast som:

(cadar x)

Som ei trening med listefunksjonar kan du prøva dette eksemplet. (Brukar du Script-Fu konsollen, må du skriva alt på ei linje). Bruk ulike variasjonar av car og cdr for å plukka ut ulike element frå lista:

        (let* (
                 (x  '( (1 2 (3 4 5) 6)  7  8  (9 10) )
                 )
              )
              ; skriv car/cdr kodane dine her
        )
      

Greier du å plukke ut talet 3 i lista med berre to funksjonskall, er du langt på vegen til å verta ein meister i Script-Fu!

[Notat] Notat

I Scheme kan du nytta semikolon (;) for å skriva ein merknad. Semikolonet, og alt som kjem etter dette på same linja, vert ignorert av skripttolkaren. Bruk dette flittig. Det er mykje enklare å finna fram i eit skript som er godt kommentert, særleg når det har gått ei tid sidan du endra det.