Lisp – Macros: costrutti di controllo standard – 2

beagle-bros-statement-of-qualityContinuo da qui, copiando da qui.

Cicli

Oltre ai costrutti di controllo visti nel post precedente ci sono i cicli, l’altro costrutto. Tutti i costrutti per i cicli sono macro, costruiti su un paio di operatori speciali che forniscono una specie primitiva di goto. Sono tagbody e go, li vedremo in futuro. Il tipo di ciclo più generico è quello di do. Benché potente per i casi normali ci sono soluzioni più semplici, dolist e dotimes. Infine la macro loop fornisce un completo mini-linguaggio à la Algol-like. Alcuni amano loop, altri lo odiano dicendo che non è lispico, vedi te!

dolist e dotimes

dolist cicla attraverso gli elementi di una lista eseguendo il corpo con una variabile contenente i successivi elementi della lista. La forma base è:

(dolist (var list-form)
  body-form*)

Quando il ciclo parte list-form è valutata per produrre una lista. Quindi il corpo del ciclo è valutato una volta per ogni elemento della lista con la variabile var avente il valore dell’elemento. Per esempio: m0 Usato in questo modo ritorna nil. È possibile interromperlo con return. m1 dotimes è per i cicli con contatore, la forma base è:

(dotimes (var count-form)
  body-form*)

count-form deve ritornare un intero. Il ciclo mette in var i valori da 0 a uno meno di quel numero, come in Python: m2anche qui è possibile usare return. Siccome il corpo di dotimes e dolist può contenere ogni tipo di espressioni si possono fare cicli annidati. per esempio le tabelline: m3

do

dotimes e dolist non sono sufficientemente flessibili per poter essere usati in tutti i cicli. Per esempio quando voglio gestire passi con variabili in parallelo o usare un’espressione generica per il test per la fine del ciclo. In questi casi si passa al più generico ciclo do. Mentre per dotimes e dolist ho una sola variabile che controlla il ciclo con do posso avere il controllo completo di come cambiarle nel ciclo. È anche possibile di definire un test che determina quando terminare il ciclo e fornire una form da eseguire alla fine che genera il valore ritornato dal do. Il modello base è:

(do (variable-definition*)
    (end-test-form result-form*)
  statement*)

Le sei parentesi del modello sono le sole richieste per il do stesso. Devi fornire una coppia per racchiudere le dichiarazioni di variabile, una coppia per il test finale e una coppia per l’intera espressione. Le forms entro il do possono richiedere –ovviamente– loro parentesi ma lo schema dev’essere sempre quello. Qualche esempio: m4 in questo caso il risultato della form è omesso e tutto equivale a un dotimes. Altro esempio, Fibonacci, calcolato senza corpo: m5 Nota: mysteryousassay 😯 Infine un esempio con un do senza variabili collegate. Cicla mentre il tempo attuale è inferiore a quello dato in una variabile globale, scrivendo “Waiting” una vola al minuto. Notare che anche senza variabile la coppia vuota di parentesi è richiesta. m6

Il grande loop

Allora… per i casi semplici ci sono dolist e dotimes, poi quando le cose sono più complesse do. Serve altro? Ci sono diversi casi in cui cicli su strutture come liste, arrays, hash tables e packages. O accumuli valori ciclando, contando, sommando, mediando e così via. Quando fai parecchie di queste cose allo stesso tempo la macro loop è quella che semplifica le cose. Nota personale: io vengo dal Fortran 😉 La macro loop esiste in due varianti: semplice e estesa. la versione semplice è semplice davvero:

(loop
  body-form*)

le forms nel corpo sono valutate ogni volta nel ciclo finché non esci con return. Per esempio il do precedente può essere così riscritto: m7 Il loop esteso è una bestia completamente differente, dice il Peter. È contraddistinto dall’uso di loop keywords che implementano un linguaggio ad-hoc per esprimere espressioni di looping. Ovvio che non tutti i lispers amino il linguaggio dell’extended loop, almeno uno dei progettisti di Common Lisp lo odiava 😉 I detrattori dicono che non è abbastanza lispico, troppe poche parentesi. I fautori dicono che in certi casi è meglio avere una sintassi più verbosa ma che rende l’idea di cosa succede. Per esempio ecco un ciclo do che mette i numeri da 1 a 10 in una lista: m8 Un lispista stagionato (cit.) non avrebbe difficoltà a capire al volo, è solo un do con nreverse e push per costruire una lista. Ma non è così trasparente. La versione con loop sembra quasi linguaggio corrente: m9 Oppure la somma dei primi 10 quadrati: m10 O il conteggio delle vocali in una stringa: m11 Il conteggio dell’undicesimo numero di Fibonacci, quello stesso del precedente do: m12 I simboli across, and, below, collecting, counting, finally, for, from, summing, then e to sono alcune delle keywords della sintassi di loop. Mi sa che di loop dovremmo parlarne ancora, molto più avanti. Ma è da notare qui come le macro possono estendere il linguaggio rendendolo più espressivo. loop ha una sua grammatica ma il resto del codice in loop è codice Lisp regolare. E anche se loop è molto più complessa di when e unless è solo un’altra macro. OK, adesso siamo pronti –dice Peter (che rockz, ogni tanto è bene ripeterlo)– per vedere più in dettaglio come fare a definire le nostre. Prossimamente… forse :mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: