Contro VisiCalc e derivati – I

sed-awkPrima ancora di cominciare due comunicazioni urgenti:

E poi anche se è un rant o una lamentela piagnucolosa sono io che sbaglio. The show must ‘ndé nans! nèh. :wink:

Allora vi racconto una storia tragica, o comica, o –OK, mia.

xls

Una mia carissima amica mi chiede di prendere i dati in colonna A e da questi estrarre le tre sottostringhe, quelle delle colonne B, C e D. Fatto, non è stato difficile, Excel –o affini, io ho usato Calc il modulo di Libre Office– ha in se un linguaggio di programmazione che si può usare.
Tutto sta a capacitarsi che invece di if devi scrivere SE e VAL.ERRORE e STRINGA.ESTRAI e simili. E poi fare riferimento alla celle. Fatta una riga poi copincolli e fatto. Quasi.
La mia soluzione (non ci ho messo molto, anzi) è quella in figura. Non si vede ma le colonne dalla B alla G sono tutte formule. Ecco, la pecca è che io mi sono preso la libertà di memorizzare i risultati intermedi nelle colonne E, F e G.
Ma sapete, ormai la programmazione funzionale colpisce anche dove non te lo aspetteresti! Anche i discendenti di VisiCalc!

Si può, certo che si può. Solo che a me viene male solo a pensarci. Ci ho provato per un po’, salvando continuamente lo step a cui ero arrivato e che stavo per distruggere (sono maldestro come pochi, davvero).

Poi ho finito per rinunciarci. Tre colonne in più non sono la fine del foglio! O forse sì ma è una cosa che davvero non ce la faccio. M’impappino con i SE VAL… e appena tenti di scrivere il valore di una cella ti mette a tradimento una coppia di parentesi che manco fossimo in Lisp. E anche a scrivere la formula in un editor normale e poi pastarla nella cella –insomma non ci sono riuscito :evil:

E in tutto questo tempo non potevo non pensare a come si sarebbe fatto una volta. Io probabilmente avrei usato AWK, ma forse invece sed. O qualcos’altro.
La prima volta che proposi al mio capo, molto tempo fa, nello scorso millennio, di usare AWK ricordo perfettamente cosa mi disse: “No, funzionerebbe, se funzionasse, solo con Unix“.

OK, ho promesso di non lamentarmi e non lo farò. Ma dovete ammettere che il file di testo con i codici sarebbe molto più piccolo e maneggevole, che lo script sarebbe di poche righe e che…
OK, largo ai giovani :mrgreen:

Lisp – Macros, definirne di nostre – 2

l1Si continua da qui, seguendo l’ottima guida, qui.

Tappare le perdite

L’espressione colorita la dobbiamo a Joel Spolsky, rende l’idea, e non c’è astrazione perfetta ma non bisogna tollerare quelle facilmente eliminabili.
Salta fuori che una macro può perdere dettagli in tre modi che però sono facilmente individuabili e correggibili.
La macro do-primes, definita nel post precedente, ne ha uno: valuta la subform end troppe volte. Se invece di chiamarla con un numero, 19 nell’esempio precedente, viene chiamata con un espressione, p.es. (random 100), così:

(do-primes (p 0 (random 100))
  (format t "~d " p))

si vede che non fa quello che deve, usiamo macroexpand-1:

l8-3

Quando il codice espanso gira random verrà chiamata ogni volta che viene valutata la condizione di fine del test. E quindi invece di testare se p è maggiore di un dato valore iniziale viene considerato un valore casuale che cambia per ogni iterazione.

Un rimedio potrebbe essere di definire questo come un comportamento di do-primes –una schifezza che va contro al Principle of Least Astonishment, principio di stupore minimo, di implementazione delle macros.
E i programmatori si aspettano che le forms passate alle macros siano valutate sono per quanto necessario. Inoltre do-primes è costruita sul modello di dotimes e dolist dove questo leak non c’è e quindi non dev’esserci neanche in do-primes.

Si può correggere facilmente; basta eseguire end una sola volta salvando il risultato in una variabile che verrà successivamente utilizzata. Ricordando che nel ciclo do le variabili vengono definite con una form di inizializzazione si può scrivere:

(defmacro do-primes ((var start end) &body body)
  `(do ((ending-value ,end)
        (,var (next-prime ,start) (next-prime (1+ ,var))))
       ((> ,var ending-value))
     ,@body))

Atz! questo introduce 2 nuovi leaks!
Uno è simile a quello corretto. Siccome le forms di inizializzazione per le variabili in un ciclo do sono valutate nell’ordine con cui le variabili sono definite quando l’espansione della macro è valutata l’espressione passata come end viene valutata come start, opposta all’ordine con cui appare nella chiamata alla macro. Quando si hanno valori come 0 e 19 non ci sono problemi ma con le forms si possono avere effetti collaterali e valutandole fuori ordine si viola il Principle of Least Astonishment.

La soluzione è di scambiare l’ordine delle valutazioni delle due variabili:

(defmacro do-primes ((var start end) &body body)
  `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
        (ending-value ,end))
       ((> ,var ending-value))
     ,@body))

L’altro leak è stato creato usando come nome di variabile endig-value. Questo nome che sembra solo un dettaglio interno dell’implementazione della macro può finire per interagire con il codice passato alla macro o nel contesto in cui la macro è chiamata. Nella seguente chiamata a do-primes sembra tutto a posto ma non funziona come ci si aspetta, a causa di questo leak:

(do-primes (ending-value 0 10)
  (print ending-value))

e neanche questa:

(let ((ending-value 0))
  (do-primes (p 0 10)
    (incf ending-value p))
  ending-value)

Al solito macroexpand-1 mostra il problema, la prima chiamata diventa:

(do ((ending-value (next-prime 0) (next-prime (1+ ending-value)))
     (ending-value 10))
    ((> ending-value ending-value))
  (print ending-value))

Qualche implementazione del Lisp può non accettare che ending-value sua usato sia come nome di variabile nel ciclo do. Se invece passa il codice gira per sempre siccome ending-value non può essere maggiore di se stesso.

Il secondo problema viene espando così:

(let ((ending-value 0))
  (do ((p (next-prime 0) (next-prime (1+ p)))
       (ending-value 10))
      ((> p ending-value))
    (incf ending-value p))
  ending-value)

In questo caso il codice è perfettamente legale ma non va perché il collegamento stabilito da let per ending-value è esterno al ciclo e viene shadoved dalla variabile con lo stesso nome nel do, la form (incf ending-value p) incrementa la variabile nel ciclo invee di quella esterna con lo stesso nome, creando un altro ciclo infinito.

Risulta chiaro che quel che serve per toppare questo leak è di usare un simbolo che non verrà mai usato all’esterno della macro. Si può tentare con un nome improbabile ma non c’è garanzia. Si potrebbero usare i packages (non ancora visti). Ma c’è una soluzione migliore.

La funzione gensym ritorna un simbolo unico ogni volta che viene chiamata. Quindi invece del nome ending-value si può generare un nuovo simbolo ogni volta che do-primes è espansa.

defmacro do-primes ((var start end) &body body)
  (let ((ending-value-name (gensym)))
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
          (,ending-value-name ,end))
         ((> ,var ,ending-value-name))
       ,@body)))

Notare che il codice che chiama gensym non fa parte dell’espansione; gira come parte del macro expander e quindi crea un nuovo simbolo ogni volta che la macro è espansa. Può sembrare strano che ending-value-name sia il nome di un’altra variabile, non diverso dal parametro var che è il nome di una variabile. la differenza è che il valore di var viene creato dal reader quando la macro viene letta mentre il valore di ending-value-name viene generato programmaticamente quando il codice della macro gira.

Con questa definizione tutto funziona, La prima form:

(do-primes (ending-value 0 10)
  (print ending-value))

espande in:

(do ((ending-value (next-prime 0) (next-prime (1+ ending-value)))
     (#:g2141 10))
    ((> ending-value #:g2141))
  (print ending-value))

Adesso la variabile usata per il valore finale è il simbolo generato #:g2141. Questo nome è stato generato da gensym ma non è importante; quello che importa è l’identità dell’oggetto del simbolo. I simboli generati da gensym sono scritti con il prefisso #:.

L’altra form problematica

(let ((ending-value 0))
  (do-primes (p 0 10)
    (incf ending-value p))
  ending-value)

viene così espansa:

(let ((ending-value 0))
  (do ((p (next-prime 0) (next-prime (1+ p)))
       (#:g2140 10))
      ((> p #:g2140))
    (incf ending-value p))
  ending-value)

E non c’è più il leak perché la variabile creata dal let non è più mascherata da una introdotta dall’espansione.

Non tutti i nomi letterali usati nella macro expansion causano necessariamente problemi –cosa che quando si diventa esperti si riesce a vedere– ma non c’è ragione per non gensym-are per sicurezza.

Con questo aggiustamento si sono tappate tutte le perdite di do-primes. Dopo aver scritto un po’ di macro si scopre che è facile a scriverle senza questi leak se si seguono queste semplici regole:

  • se non ci sono particolari ragioni per fare diversamente, includere ogni subforms nell’espansione nella posizione che sarà valutata nello stesso ordine in cui appare  nella chiamata alla macro;
  • se non ci sono particolari ragioni per fare diversamente, assicurarsi ce le subforms siano valutate una volta sola creando una variabile nell’espansione per contenere il valore della valutazione della form e usare questa variabile ovunque il valore dell’espansione è richiesto;
  • usare gensym nell’espansione della macro per creare nomi di variabile usate nell’espansione.

OK, adesso –no pausa :mrgreen:

Lisp – Macros, definirne di nostre – 1

l0Adesso, dice Peter, è ora di definire le nostre macro. Panico :roll: no, ormai dai :grin:. La difficoltà principale di capire le macro è forse che sono troppo integrate nel linguaggio. Ma Peter non si stanca di ripetere che “macros operate at a different level than functions and create a totally different kind of abstraction“. E ancora:

Once you understand the difference between macros and functions, the tight integration of macros in the language will be a huge benefit. But in the meantime, it’s a frequent source of confusion for new Lispers. The following story, while not true in a historical or technical sense, tries to alleviate the confusion by giving you a way to think about how macros work.

La storia di Mac, solo una storia

Premessa mia: Mac non è quello là; è una delle prime versioni al MIT, forse vuol dire Men And Computers, forse qualcos’altro. Allora non c’erano le macro e si doveva riscrivere e a quei tempi anche il copiancolla e –OK, la storiella è divertente, Peter la racconta benissimo, leggetela da lui. Ha anche un bel finale :lol:

Macro Expansion Time vs. Runtime

Il segreto di capire le macro è di aver chiara la distinzione quando il codice che genera codice (quello della macro) e il codice che finisce per diventare il programma (tutto il resto). Quando si scrive una macro si scrive codice che sarà usato dal compilatore per generare codice che sarà compilato. Solo dopo che tutte le macros sono espanse e il codice risultante è compilato il programma può girare. Il momento in cui le macros girano è chiamato macro expansion time; è distinto dal runtime quello in cui il codice –incluso quello generato dalle macro– gira. La distinzione è importante –e facile, la do per scontata :roll: Supponiamo che nel programma ci sia:

(defun foo (x)
  (when (> x 10) (print 'big)))

Normalmente viene da pensare a x come una variabile con un valore che viene passato a foo. Ma quando la macro viene espansa, vale a dire quando il compilatore fa girare la macro when, è disponibile solo il codice sorgente. Siccome il programma non sta girando non c’è nessun valore associato a x. Supponiamo che when sia (come abbiamo già visto) così definita:

(defmacro when (condition &rest body)
  `(if ,condition (progn ,@body)))

Quando il codice viene compilato la macro when viene eseguita con queste due forms come argomenti. Il parametro condition viene collegato a (> x 10) e la form (print 'big) viene raccolta in una lista che diventa il valore del corpo del parametro &rest. La backquote genererà questo codice: (if (> x 10) (progn (print 'big))) interpolando tra il valore di condition e unendo i valori del corpo con progn. Quando il Lisp è interpretato, invece di compilato, la distinzione tra i tempi di espansione delle macros e quello di runtime è meno chiara perché collegate. Inoltre il linguaggio non specifica come devono essere trattate le macro ma in ogni caso who cares come direbbero i west-padagni indigeni locali di qui :wink:

defmacro

Le macros vengono definite con defmacro che sta per DEFine MACRO; lo schema base è simile a quello di defun:

(defmacro name (parameter*)
  "Optional documentation string."
  body-form*)

Siccome le macros possono usare tutto quanto il Lisp qui ce ne sarà solo un pochino ma verrà descritto il processo per scrivere macros, dalla più semplice alla più complessa. Il compito di una macro è di tradurre una macro form in codice che fa una particolare cosa. Normalmente si parte scrivendo il codice che si vuole scrivere, cioè con un esempio della macro form. Altre volte si decide di scrivere la macro dopo aver scritto più volte lo stesso codice e si capisce che è il momento di astrarre. In ogni caso devi sapere sia da dove vieni e dove vuoi arrivare prima di sperare di scrivere il codice che lo fa automaticamente. Allora il primo passo è di scrivere almeno un esempio di una chiamata alla macro e di come sarà espansa. Quando si ha l’esempio di chiamata e di come viene espansa si passa al secondo step: scrivere il codice della macro. Per una macro semplice questo si riduce a scrivere un backquoted template con i parametri inseriti nel loro posto. Le macros complesse sono molto più impegnative. :evil: Dopo aver scritto il codice per tradurre la chiamata d’esempio nell’espansione appropriata ti devi assicurare che l’astrazione non perda (leak) dettagli nella sua implementazione. Con un’astrazione leaky la macro funziona con certi argomenti ma con altri interagisce con l’ambiente in modo indesiderato. Finisce che le macros possono perdere in diversi modi, tutti che possono essere facilmente evitati se si sa come controllarne la presenza. Cosa che verrà discussa in “Tappare le perdite” Nota: detesto queste metafore da idraulico, non sono Mario. Riassumendo, i passi per scrivere una macro sono:

  • scrivere una semplice chiamata e il codice che dev’essere espando, o vice versa;
  • scrivere il codice che genera l’espansione scritta a mano nell’esempio di chiamata;
  • assicurarsi che l’astrazione della macro non perda.

Una macro d’esempio: do-primes

Per vedere come i tre passi enunciati vengono messi in pratica scriviamo la macro do-primes che con un ciclo similare a dotimes e dolist eccetto che invece di iterare con interi o elementi di una lista itera per numeri primi successivi. Non è una macro particolarmente utile, solo un esempio per vedere come si fa. Per prima cosa servono due funzioni d’utilità, una che ci dice se un numero è primo e un’altra che ci ritorna il primo successivo maggiore o uguale ai suoi argomenti. In entrambi i casi si può ricorrere all’inefficiente forza bruta:

(defun primep (number)
  (when (> number 1)
    (loop for fac from 2 to (isqrt number) never (zerop (mod number fac)))))

(defun next-prime (number)
  (loop for n from number when (primep n) return n))

Nota mia: primep andrebbe esaminata attentamente :roll: Adesso si può scrivere la macro. Seguendo la procedura definita prima ci serve un esempio di chiamata e di espansione. Ecco:

(do-primes (p 0 19)
  (format t "~d " p))

per esprimere un ciclo che esegue il corpo una volta per ognuno dei primi maggiori o uguali a 0 e minori o uguali a 19, con la variabile p che contiene il numero primo. Conviene seguire il modello delle macro dotimes e dolist; le macros che seguono un modello di macros esistenti sono più facili da capire di quelle che introducono una sintassi nuova. Senza la macro do-primes si potrebbe scrivere un ciclo con do e le due utility definite prima come questo:

(do ((p (next-prime 0) (next-prime (1+ p))))
    ((> p 19))
  (format t "~d " p))

Siamo adesso pronti per scrivere il codice che traduce dal precedente a quest’ultimo.

Parametri delle macros

Siccome gli argomenti passati a una macro sono oggetti Lisp rappresentanti il codice sorgente della chiamata il primo passo per ogni macro è di estrarre quali sono le parti che servono per calcolare l’espansione. Per macros che interpolano semplicemente i loro argomenti in un template questo passo è banale: definire i parametri giusti per contenere i differenti argomenti è sufficiente. Ma questo approccio non sembra essere sufficiente per do-primes. Il primo argomento nella chiamata a do-primes è una lista contenente il nome della variabile del ciclo, p; il limite inferiore, 0; e il limite superiore, 19. Ma guardando all’espansione la lista come tale non appare nell’espansione; i tre elementi sono divisi e messi in posti diversi. Si può definire do-primes con due parametri, uno per contenere il primo e un parametro &rest per le forms del corpo, e quindi dividere la lista, in qualcosa di simile:

(defmacro do-primes (var-and-range &rest body)
  (let ((var (first var-and-range))
        (start (second var-and-range))
        (end (third var-and-range)))
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var))))
         ((> ,var ,end))
       ,@body)))

Tra un attimo la spiegazione di come il corpo generi l’espansione corretta; per adesso si noti come var, start e end hanno ognuna il valore, estratto da var-and-range, che viene interpolato nella backquote che genera l’espansione di do-primes. Tuttavia non è necessario dividere var-and-range a mano perché le liste dei parametri delle macro sono quelle che vengono chiamate destructuring parameter lists. Come il nome suggerisce involvono il suddividere la struttura, in questo caso la lista delle forms passate alla macro. Con una destructuring parameter list un semplice parametro può essere rimpiazzato in una lista di parametri annidata. I parametri nella lista di parametri annidata prendono i loro valori dagli elementi dell’espressione che sarebbero stati collegati nella lista rimpiazzata. Per dire si può rimpiazzare var-and-range con la lista (var start end) e i tre elementi verranno automaticamente destrutturati in questi tre parametri. Un’altra caratteristica speciale delle macro parameter lists è che si può usare &body come sinonimo di &rest. Semanticamente &body e &rest sono equivalenti ma diversi ambienti usano &body per modificare l’indentazione usata per le macro. Così si può snellire la definizione di do-primes e dare un suggerimento sia al programmatore che legge sia al tool di sviluppo definendo la macro così:

(defmacro do-primes ((var start end) &body body)
  `(do ((,var (next-prime ,start) (next-prime (1+ ,var))))
       ((> ,var ,end))
     ,@body))

Oltre che essere più concisa la destructuring parameter lists esegue un controllo automatico degli errori. Con do-primes definito in questo modo Lisp sarà capace di trovare le chiamate in cui il primo argomento non è una lista di tre elementi e dare un messaggio d’errore chiaro di chiamata a funzione con troppi pochi argomenti. Inoltre se si unsa SLIME (Emacs) che i indica quali argomenti ci si aspettano appena scrivi il nome della funzione o macro con la destructuring parameter lists sarà in grado di essere più esplicito della sintassi della chiamata alla macro. Con la definizione originale SLIME direbbe che do-primes è chiamata come: (do-primes var-and-range &rest body) ma con la nuova definizione può irti che dev’essere così: (do-primes (var start end) &body body) Le destructuring parameter lists possono contenere parametri &optional, &key e &rest e destructuring parameter lists annidate. Cose non richieste nel nostro esempio.

Generare l’espansione

Siccome do-primes è semplice e abbiamo destrutturato gli argomenti resta solo da interpolarli nel template per avere l’espansione. Per le macro semplici la sintassi di backquote è perfetta. Ci ricordiamo vero che un’espressione backquotata è come una quotata tranne che si può squotare una subespressione precedendola con una virgola e per le liste un ,@. Un altro modo di vedere la backquote è come metodo conciso di scrivere codice che genera liste. Questo è esattamente quel che succede quando il reader legge l’espressione backquotata, la traduce nel codice che genera la struttura appropriata. Per esempio `(,a b) può essere letta come (list a 'b). Lo standard del linguaggio non specifica cosa il reader deve produrre. Nota: capito poco. Ecco una tabella con alcuni esempi

Backquote Syntax    Equivalent List-Building Code  Result
`(a (+ 1 2) c)	    (list 'a '(+ 1 2) 'c)	   (a (+ 1 2) c)
`(a ,(+ 1 2) c)	    (list 'a (+ 1 2) 'c)	   (a 3 c)
`(a (list 1 2) c)   (list 'a '(list 1 2) 'c)	   (a (list 1 2) c)
`(a ,(list 1 2) c)  (list 'a (list 1 2) 'c)	   (a (1 2) c)
`(a ,@(list 1 2) c) (append (list 'a) (list 1 2)   (list 'c)) (a 1 2 c)

È importante notare che la backquote è solo una convenienza ma ne è una grossa. Per apprezzarla compariamo la versione backquotata di do-primes data sopra con quella che usa le liste esplicite:

(defmacro do-primes-a ((var start end) &body body)
  (append '(do)
          (list  (list (list var
                             (list 'next-prime start)
                             (list 'next-prime (list '1+ var)))))
          (list (list (list '> var end)))
          body))

Vedremo prossimamente che l’implementazione corrente di do-primes non gestisce correttamente un certo caso limite. Ma per iniziare vediamo che funzioni per l’esempio originale. si può fare il test in due modi. si può provare se va, ecco: l8-0 OK. Oppure si può controllare la macro direttamente nella sua espansione di una particolare chiamata. La funzione macroexpand-1 prende una qualsiasi espressione Lisp come argomento e ritorna il risultato di un livello di espansione di macro. C’è anche la funzione macroexpand che continua a espandere ma visualizza molti più dettagli a basso livello, panicosa :wink: Poiché macroexpand-1 è una funzione per passargli un argomento letterale bisogna quotarlo. Provo: l8-1 OK. Se si usasse SLIME (mi sa che prima o poi…) poni il cursore nella parentesi aprente la fporm della macro e digiti C-c RET e il risultato è stampato in un buffer. In ogni caso sembra che tutto funzioni. Ma … –prossimamente :mrgreen:

Monkeys

monkey_faceUn post non tanto serio (o serioso) ma ogni tanto –semel in anno …, avrebbe detto la Berta, la prof di ‘taliano, non troppo saepe, nèh– su una cosa che non so bene ma istruttiva –forse. O forse no.

Insomma c’è questa storia delle scimmie. Che ti ritrovi da tutte le parti, ce ne sono di davvero divertenti, in barili.

peterTutto è esploso con il Lisp, Common Lisp. E Peter Seibel. In effetti mi sento in colpa: sto copiando dal suo Practical Common Lisp come se –ecco troppo! Vale dire che è fatto molto bene, completo. Sto capendo tutto, anche se sono niubbo assay e anche non troppo smart. Fatto sta ed è che il sito di Peter si chiama Gigamonkeys, e lì trovate anche il perché.

Poi ci sono altri gigamonkeys on-teh-toobz, ecco qualche logos, uncorrelati:

logos
Ma forse no, c’è altro, davvero. In questi casi Urban Dictionary è una miniera. Adesso non voglio fare quello che linka tutto, potete –se avete la mia stessa fisima– cercare voi, io riassumo.

udEcco una parola di successo, di quelle che uno proprio non può farne a meno.

clouseau

Visto nel Web – 175

Puntuale come un Rolex a testa alta ecco cosa ho visto nel Web.

MIT Launches Three-pronged Effort To Thwart Cyber Attacks
::: Slashdot

Whitehouse.gov Is Now Secure By Default, Signaling a Trend in Government
::: Motherboard

L’Internet delle cose
::: ElectroYou

USEFUL ONE-LINE SCRIPTS FOR SED
::: sed

4chan’s Overlord Christopher Poole Reveals Why He Walked Away
::: Rolling Stone

maurizio

Early wearable computer: Qing Dynasty abacus ring
::: BaxterSally

Nancy Drew and the Case of the Slow Program
altamente istruttivo
::: Julia Evans

Continue statements with Labels in Go (golang)
::: Repeatable Systems

Ognuno ha il suo sticky bit che si merita!
Lubit cresce, bene :grin:
::: the secrets of ubuntu

Why is it hard to write a compiler for Perl 6?
::: steveklabnik

1329151-004-U430304105092110QB-U430702588488417YG-593x443@Corriere-Web-Nazionale

This App Lets You Piggyback Facebook’s Free Internet To Access Any Site
::: Slashdot

The GNU Manifesto Turns Thirty
::: The New Yorker

Addomesticare PowerPoint in 10 mosse
::: Sergio Gridelli

GXUI – A Go cross platform UI library
::: google

Smartphones set to boost large-scale health studies
::: Nature

tumblr_nlct2rkvcd1qz5v2lo1_500

Not Quite Dead: SCO Linux Suit Against IBM Stirs In Utah
::: Slashdot

Congratulations to @Wikimedia for adopting #OA policy for funded research – great example for other private funders
::: communia_eu

Survey: C++ is the most valuable software engineering skill
::: ITworld

Microsoft is open sourcing Visual Studio’s build tool, MSBuild
::: The Next Web

Dale
Lisp-flavoured C
::: tomhrr

21114_10205118677599977_3863802669928651831_n

Come Programmare Su Linux
::: Andrea M

Il Paper inviato dal Governo Renzi alla Commissione Europea. La posizione Italiana sul digital single MARKET
::: FULOG

Pensare in Python – Come pensare da Informatico, la nuova versione italiana
::: Distillato di Python

Primo studio per il simbolo
::: ulaulaman ::: Flavio_MfM

#Lubit6 #LubitProject
::: bit3lux

11075192_1049690921714835_9049442868188114297_n

Il @pewresearch sugli effetti di Internet nei paesi in via di sviluppo
::: fabiochiusi

Point to point wiring of LISP
::: walltechOSHW

È colpa tua @google :(
::: SteMicDM

Writing And Using C Code From Go
::: Karl Seguin

Clojure to JS compiler
::: clojure

162438494-09de8be5-f45b-4edc-9f33-e08dd2850ed4

Defending Privacy Doesn’t Pay: Canadian Court Lets Copyright Troll Off the Hook
::: Slashdot

Linux’s worst-case scenario: Windows 10 makes Secure Boot mandatory, locks out other operating systems
::: ExtremeTech

RMS, do you have an opinion about systemd?
::: johns_FSF

Virtual Keypunch
type and download your personal punch cards
::: mass:werk
11026318_961750537203359_7264993884924288622_n

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 :shock: 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 :wink: 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 :wink: 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:

Lisp – Macros: costrutti di controllo standard – 1

lIl titolo l’ha messo Peter, io sarei più sul semplice, mica sono così smart :wink:
E il titolo speciale è perché si parla di una cosa speciale del Lisp, che è tutto speciale fià di suo. Sì le macro come le ha il Lisp non le ha nessuno (non contano le imitazioni).

Spiegare cosa siano le macro non è la cosa più semplice, come gli OGM soffrono di pregiudizi ma dai, pronti? via!

È normale che con ogni linguaggio di programmazione oltre al core ci siano librerie, che implementano un po’ tutto quello che serve. Uno dei vantaggi di questo approccio è che è più facile da implementare e comprendere. E, il vantaggio reale, è estensibile, cosa che per il Common Lisp lo fanno le macro.
Abbiamo già visto brevemente che ogni macro definisce la sua propria sintassi, determinando come le s-expressions sono passate alle Lisp forms. Con le macro del core è possibile creare nuove sintassi –costrutti di controllo come when, dolist e loop e di definizione di forms come defun e defparameter– come parte della standard library invece di doverle inserire dentro il core.
Salto qui una tiratina contro la macrophobia derivata da C e dintorni :roll: Potete sempre –volendo– leggerla di là.

when e unless

Abbiamo già visto la forma condizionale base: if:

(if condition then-form [else-form])

condition è valutata e, se non-nil, la then-form è valutata e il suo valore ritornato; altrimenti l’else-form è valutata e il suo valore ritornato. Se condition è nil e non c’è else-form il risultato di if è nil.

(if (> 2 3) "Yup" "Nope") ==> "Nope"
(if (> 2 3) "Yup")        ==> NIL
(if (> 3 2) "Yup" "Nope") ==> "Yup"

Tuttavia if non è così bella come vorremmo perché then-form e else-form sono confinate entro un unica form. Questo comporta difficoltà quando voglio effettuare più azioni, per esempio, caso di trattamento dello spam devo scrivere (semplifico qui, Peter la fa più lunga), usando progn che esegue tutte le sue form tornando il valore dell’ultima:

(if (spam-p current-message)
    (progn
      (file-in-spam-folder current-message)
      (update-spam-database current-message)))

OK, si può fare ma siccome è una cosa abbastanza comune perché non fare qualcosa? Ecco, c’è when che consente di scrivere:

(when (spam-p current-message)
  (file-in-spam-folder current-message)
  (update-spam-database current-message))

Ma se non fosse definita nella standard library si potrebbe definire when, usando la backquote già vista anticamente:

(defmacro when (condition &rest body)
  `(if ,condition (progn ,@body)))

Nota: in realtà non si può ridefinire una macro della standard library, volendo proprio usare un altro nome, p.es. my-when.

Il complemento di when è unless, che fa l’opposto, valuta il suo corpo se condition è falsa.

(defmacro unless (condition &rest body)
  `(if (not ,condition) (progn ,@body)))

OK, sono macro davvero semplici, ma fanno il loro lavoro semplificando il codice. E ci fanno vedere come anche noi possiamo crearne di simili.

cond

Un altro caso dove if è insufficiente è quando ci sono più scelte: if a fai x, altrimenti if b fai y altrimenti fai z. Si può fare, così:

(if a
    (do-x)
    (if b
       (do-y)
       (do-z)))

ma –come dire– e probabilmente devi metterci di mezzo anche progn. Ovvio quindi che ci sia una macro per la condizione multipla cond:

(cond
  (test-1 form*)
      .
      .
      .
  (test-N form*))

Ogni elemento del corpo rappresenta una delle scelte della condizione e consiste in una lista contenente una form di condizione e zero o più forms che sono valutate se la scelta è scelta (ehmm…). Le condizioni sono valutate nell’ordine in cui compaiono finché una risulta vera. A questo punto le altre forms della scelta sono valutate e l’ultima è ritornata come valore della cond. Se non ci sono forms nella scelta è questa che viene ritornata. Per convenzione l’ultima scelta rappresentante l’altrimenti finale della catena di scelte è scritta come t. Ogni valore non-nil funzionerebbe ma così si vede chiaramente quando si legge il codice.
Quindi la precedente if annidata si può riscrivere così:

(cond (a (do-x))
      (b (do-y))
      (t (do-z)))

Scrivendo condizioni per if, when, unless e cond salta fuori l’utilità degli operatori logici and, or e not.

not (è una funzione) che inverte il valore della verità, tornando t se nil e nil se t.

and e or sono macro implementate come congiunzione e disgiunzione logiche di qualsiasi numero di subforms; sono definite come short-circuit, quindi ne sono valutate solo quanto serve. Quindi and torna nil non appena vede un nil; se nessuna vale nil torna il valore dell’ultima. or invece si ferma appena una vale non-nil e ne ritorna il valore. Se nessuna è vera torna nil. Esempi:

(not nil)             ==> t
(not (= 1 1))         ==> nil
(and (= 1 2) (= 3 3)) ==> nil
(or (= 1 2) (= 3 3))  ==> t

OK, adesso i cicli — no! pausa :mrgreen:

Lisp – variabili – 3

r3Indovina? Sì, Peter qui e il blog qui.

Costanti

C’è un altro tipo di variabile ancora: la constant variable.
Tutte le costanti sono globali e sono definite con defconstant.

(defconstant name initial-value-form [ documentation-string ])

Non possono essere passate come parametri di funzione, non possono essere ridefinite. La convenzione è di iniziare e finire il loro nome con +, meno seguita di quella relativa a *. Anche le costanti predefinite nil, t e pi non la seguono.
Saltano fuori problemi quando si vuole ridefinirne una; nel dubbio usare defparameter.

Assegnamento

Creato un binding posso farne due cose: prenderne il valore e dargliene uno nuovo. Per assegnare un nuovo valore posso usare la macro setf:

(setf place value)

Siccome setf è una macro può esaminare place prima di assegnarle il valore value. Se place è una variabile la macro si espande e chiama l’operatore speciale setq che (essendo un operatore speciale) ha accesso sia al binding lessicale che a quello dinamico. Nel Lisp moderno non si usa più accedere direttamente a setq. Per esempio per assegnare 10 alla variabile x:

(setf x 10)

Ovvio che questo non ha effetto su ogni altro binding; quindi:

(defun foo (x) (setf x 10))

non ha nessun effetto fuori da foo. Esempio:

v19

setf può assegnare più posti, p.es. invece di:

(setf x 1)
(setf y 2)

posso scrivere:

(setf x 1 y 2)

cosa che io sono abituato a scrivere così:

(setf x 1 
      y 2)

setf ritorna il valore assegnato per cui se ne possono annidare le chiamate, es:

v20

Nota: a volte la REPL non fa quello che voglio: p.es. qui ho dovuto definire x e y prima di di poterle usare :evil:

Assegnamento generalizzato

I variable bindings non sono l’unico posto deve posso memorizzare valori. Common Lisp supporta strutture composte quali arrays, hash tables e liste oltre alle strutture di dati user-defined, tutte con più posti che contengono un valore.
Se ne parlerà poi ma intanto ma c’è roba tipo defsetf e define-setf-expander, #sapevatelo.

setf è come = dei linguaggi normali, quelli che fanno robe come in tabella:


Assigning to ...	  Java, C, C++	  Perl	               Python
... variable	          x = 10;	  $x = 10;	       x = 10
... array element	  a[0] = 10;	  $a[0] = 10;	       a[0] = 10
... hash table entry      --	          $hash{'key'} = 10;   hash['key'] = 10
... field in object	  o.field = 10;   $o->{'field'} = 10;  o.field = 10

setf funziona allo stesso modo: il primo argomento è il posto dove memorizzare il valore, il secondo il valore. Come per = si può usare la stessa forma per esprimere il posto dove inserire il valore. pertanto l’equivalente Lisp della tabella –date aref per accedere agli array e gethash per le hash tables si ha:

Simple variable:    (setf x 10) 
Array:              (setf (aref a 0) 10)
Hash table:         (setf (gethash 'key hash) 10)
Slot named 'field': (setf (field o) 10)

Nota: al solito, sono ancora troppo niubbo :roll:

Altri modi per modificare i posti

Ci sono certi tipi di operazioni comuni per modificare il valore di una variabile che rendono sensato un loro operatore. Per esempio invece di incrementare con (setf x (+ x 1)) o decrementare con (setf x (- x 1)) si possono usare incf e decf che incrementano e decrementano per un certo valore,default 1:

(incf x)    === (setf x (+ x 1))
(decf x)    === (setf x (- x 1))
(incf x 10) === (setf x (+ x 10))

incf e decf sono macro del tipo modify macros; hanno il pregio di essere più concise dell’uso di setf. inoltre si possono usare per cose come

(incf (aref *array* (random (length *array*))))

che incrementa il valore di un arbitrario elemento dell’array. Senza usare incf potrebbe pensare di scrivere:

(setf (aref *array* (random (length *array*)))
      (1+ (aref *array* (random (length *array*)))))

che non funziona perché le 2 chiamate a random non necessariamente ritornano lo stesso valore. Invece l’espressione incf funziona perché sa su quale elemento sta operando. Quindi, senza incf, si dovrebbe scrivere:

(let ((tmp (random (length *array*))))
  (setf (aref *array* tmp) (1+ (aref *array* tmp))))

In generale le modify macros garantiscono di valutare i loro argomenti e le subforms una sola volta, da sinistra a destra.

La macro push, già usata nell’esercizio del DB, è un’altra modify macro. Vedremo prossimamente i suoi parenti pop e pushnew.
Infine qualcosa di più esoterico: rotatef e shiftf:

v21

che sarebbe poi equivalente a

(let ((tmp a)) (setf a b b 10) tmp)

v22

Sia rotatef che shiftf possono essere usate con qualsiasi numero di argomenti e garantiscono la valutazione ua sola volta, da sinistra a destra come tutte le modify macros.

OK, pronti per le macro? prossimamente, adesso pausa :mrgreen:

Scambiare documenti con il mondo, o una piccola parte di esso

scambioPost in collaborazione con GG (spero di averlo spesso qui sul blog), lui è il responsabile della parte seria, io di quella vecchia e lamentosa :roll:
Mi capita spesso a volte di scrivere qualcosa che è solo una bozza, assolutamente non definitiva, a volte solo idee buttate giù al volo. E neanche per me.
Siccome io sono vecchio ho abitudini diverse da quelle odierne, roba d’antiquariato. Per esempio quella cosa che “everything is a file” che poi viene da pensare che tutto sia un text file e che l’editor sia centrale, sia esso ed, vi, Emacs, Gedit o altro ancora –whatever :wink:
E capitano cose che voi umani sapete benissimo, provo a giustificarmi.
Per lo sfogo che segue potevo scrivere qualsiasi cosa ma abbiamo fatto di meglio: preso l’inizio di un racconto, bello, lo trovate qui.
Ho copiato l’inizio in un file di testo, come faccio di solito quando scrivo, niente M$Word o simili, solo .txt. Il bello dei file di testo è che puoi vederli nel terminale con il comando cat. No, non ho usato cat perché l’output sarebbe stato troppo lungo, ho usato head, chiedendogli di scrivere solo le prime 4 righe, ecco:

t0Tutto bene –sembra– posso passarlo al mondo. Ora il mondo usa (quasi tutto) Windows, 7, 8, 8.1 e altro ancora (qualcuno ancora XP, qualcuno anche Vista (addirittura!)).
E qui capitano i guai. Cioè si scopre che sono vecchio e superato, come adesso vi faccio vedere.
Di solito mi dimentico di mettere l’estensione .txt; poi su Windows non si vedrà e saranno dialoghi surreali, in questo caso invece era OK. Allora ecco con Blocco note (Notepad):

bnNo, decisamente no. Ma aspetta, vai in Formato e seleziona A capo automatico e, come per magia, ecco:

bn1Mmmmh, il testo c’è ma non va a capo. Oops! colpa mia che ho lasciato l‘a-capo di Unix/Linux; potevo cambiarlo ma me ne dimentico sempre. A dire il vero il Blocco note è quello che è, ci sono in giro sostituti freeware decisamente migliori ma bisogna installarli e … la solita storia. Anche perché con Windows ci sono millemila virus sempre in agguato.

E se si provasse a visualizzare questo testo nel browser, quello che usi per il Web? No, non funziona. Perché il fatto che la codifica sia UTF-8 (Unicode) non è scritta da nessuna parte, ecco cosa ottengo con firefox, con Linux:

browsere con Windows:
browLa stessa cosa con Chrome.

Ma una soluzione –relativamente semplice e indolore– c’è: il PDF. Basta stampare il documento dicendogli che la stampante è un file PDF (potrebbe essere anche un Postscript (PS) ma è cosa solo nostra) e tutto torna:

pdfQui è su Linux ma è identico con Windows. E posso selezionare i pezzi che voglio e copincollarli. E mettergli i font che mi piacciono, Juhan queste cose proprio non le capisce (no! è che non può, poi te lo spiego).

Caso risolto. Con una considerazione finale. Agli albori del Web, diciamo attorno al ’94-95 quando era una cosa nuovissima capitava di dover raccontare anche di quello.
C’erano, da noi, specie a scuola, idee diffuse non tanto belle, per esempio i frame.
Qualcuno li ricorda? Oggi sono molto rari, roba così:

framesIo ero contrarissimo. E avevo un alleato: Jakob Nielsen; anzi ero conosciuto come nilseniano.
Adesso il sito di Jakob non è più quello, si chiamava Use It, adesso è integrato in NNG, validissimo.
Ma la cosa buffa è che Jakob era assolutamente contrario ai PDF: sono un formato di stampa, non per altre cose, diceva. Ecco, non è più vero.

Lisp – variabili – 2

r2
Ebbene sì sempre con il Peter :grin:, continuando da qui.

Variabili lessicali e closures

Per default le variabili di Common Lisp sono lexically scoped: possono cioè essere riferite solo nella form che le definisce, la binding form. Un po’ come le variabili locali dei linguaggi normali (cioè non super-hyper-über, cioè non Lisp). E Peter ci ricorda che il lexical scoping è stato introdotto da Algol (antenato del Pascal, anzi di quasi tutto) negli anni ’60.

Tuttavia, lo sapete che il Lisp è speciale, vero? e le variabili lessicali quindi anche, hanno un twist (dopo, dopo :wink:). Il twist viene ottenuto con la combinazione del lexical scope e funzioni annidate. Secondo le regole solo il codice entro la binding form può trattare una variabile lessicale. Però se una funzione anonima contiene un riferimento a una variabile lessicale dentro uno scope incluso cosa capita? Per esempio in questa espressione:

(let ((count 0)) #'(lambda () (setf count (1+ count))))

v6

Il riferimento a count dentro la lambda dev’essere legale per le regole del lexical scoping. Il valore della funzione anonima può essere ritornato come valore della let e può essere invocato con la funcall. E funziona:

v7

Per capire come le closures funzionano occorre pensare che è il collegamento e non il valore della variabile che viene catturato. Quindi una closure può non solo accedere al valore della variabile che chiude ma può altresì assegnarle nuovi valori che persistono per le chiamate successive. Per esempio catturando la precedente closure in una variabile globale si ha:

v8

Una singola closure può chiudere su più variabili riferendosi ad esse. E più closures possono catturare lo stesso collegamento. Per esempio l’espressione che segue ritorna una lista di 3 closures: una che incrementa il valore del collegamento a count, una che lo decrementa e una che ne ritorna il valore:

(let ((count 0))
  (list
   #'(lambda () (incf count))
   #'(lambda () (decf count))
   #'(lambda () count)))

(Uhmmm… non mi funziona, ci devo pensare su).

Variabili dinamiche (dette anche speciali)

Limitando lo scope di una variabile si semplifica il codice, cosa che fanno tutti i linguaggi con le variabili locali, come le nostre lessicali. Capitano però dei casi in cui servono variabili globali. OK, qui fate conto che ci sia la solita tirata :roll: che vi risparmio perché sono fondamentalmente non ripetitivo più del necessario.

Common Lisp ha 2 modi per creare variabili globali: defvar e defparameter. Entrambe le form hanno un nome di variabile, un valore iniziale e una stringa di documentazione, questa opzionale. Si usa definire i nomi con * a inizio e fine. Esempi:

(defvar *count* 0
  "Count of widgets made so far.")

(defparameter *gap-tolerance* 0.001
  "Tolerance to be allowed in widget gaps.")

Con defparameter devo sempre assegnare un valore, con defvar posso ometterlo e diventa una variabile unbound. Ovvio quando uso una o l’altra, dai!

dopo aver definito la variabile *count* posso incrementarne il valore con questa funzione:

v9

Parecchi linguaggi memorizzano lo standard input  output in variabili globali, che funziona. Ma se poi si vuole cambiare temporaneamente si sarebbe tentati (io sì p.es.) di modificare questa variabile. Per esempio come al solito scrivo sul terminale, via *standard-output* ma se voglio salvare un pezzo di output su file posso modificare *standard-output*; ma se dopo mi dimentico i resettare tutto finirà anche nel file. Inoltre se il programma è multithread capitano guai. Quello che si vuole è di riservare per un pezzo di codice di usare un nuovo valore per *standard-output*; quando questo pezzo finisce la variabile *standard-output* deve tornare automaticamente al valore usuale.

Common Lisp fa esattamente questa cosa con l’altro tipo di variabili dinamiche, con let.
Quando si collega dinamicamente (con let) una variabile dinamica a una variabile o function parameter il collegamento che viene creato rimpiazza il collegamento globale per la durata della binding form. Diversamente dal collegamento lessicale che può essere referenziato solo dal codice dentro lo scopo della form un collegamento dinamico può referenziarsi a qualunque codice che viene invocato durante l’esecuzione della binding form (detto extent della form). Per questo aspetto risulta che tutte le variabili globali sono in effetti variabili dinamiche. Da pensarci la notte invece di contare le pecore :roll:

Tornando a noi, se si vuole ridefinire temporaneamente *standard-output* il modo è semplicemente di ricollegarlo (rebind) con un let:

(let ((*standard-output* *some-other-stream*))
  (stuff))

e tutto il codice risultante dalla chiamata a stuff che fa riferimento a *standard-output* userà il collegamento stabilito dalla let. E quando questa finisce *standard-output* ritorna automaticamente quello precedente a let. In ogni momento ogni variabile dinamica data nasconde (shadow) il binding precedente. Concettualmente per ogni nuovo binding le variabili vengono inserite in uno stack e estratte quando le forms ritornano.

Forse è il caso di fare un esempio, vero? Eccolo:

v10

La defvar ha creato *x* con il valore 10; come si può verificare:

v11

Con una let creo un nuovo collegamento che shadow il precedente:

v12

chiamando nuovamente foo, fuori dalla let il valore è di nuovo quello iniziale:

v13

Definisco adesso una nuova funzione:

v14

Notare la chiamata a foo all’interno della let che ridefinisce *x* tra le due chiamate all’esterno della let, ovviamente si ha:

v15

dai, facile, l’ho capito anch’io :roll:

Anche con il collegamento lessicale, l’assegnamento a un nuovo valore ha effetto solo per il binding corrente. Per vederlo ridefiniamo foo per includere un assegnamento a *x*.

v16

Adesso foo scrive il valore di *x*, lo incrementa e lo riscrive. Ecco:

v17

Niente di sorprendente ma ecco bar:

v18

Uepp! *x* è partito con 11, l’ultimo valore dalla precedente chiamata, poi in foo viene incrementato a 12. All’interno della let non vengono visti i valori globali. L’ultima chiamata a foo usa di nuovo il valore globale (12 che porta a 13).

Uh! come fa let a conoscere che quando crea il collegamento a *x* deve crearne uno dinamico invece di un normale lessicale? Lo sa perché il nome è stato definito speciale (ecco il motivo per cui le variabili dinamiche sono a volte dette speciali). Il nome definito con defvar e defparameter sono automaticamente dichiarati globalmente speciali. Questo vuol dire che quando si usa questo nome in una let o come parametro per una funzione o ogni altro costrutto che crea una nuova variabile il collegamento che crea è dinamico. Ecco perché la *convenzione* dei *nomi* è così importante. OK, tirata che salto :lol:

È anche possibile dichiarare una variabile localmente speciale. Se in una binding form crei un nome speciale il binding creato per quella variabile sarà dinamico. Altro codice può localmente dichiarare un nome speciale per riferirsi al binding dinamico. Tuttavia, cito: “locally special variables are relatively rare, so you needn’t worry about them“. Se proprio vuoi complicarti la vita vedi declare, special e locally.

Il binding dinamico rende le variabili globali molto più maneggevoli ma ci sono sempre i soliti rischi: usare con cautela, sapendo quello che si fa :grin:

Pausa :evil:

Sì subito ma prima un aneddoto. A volte (OK, spesso) mi blocco con “come si dice“; ecco cos’è è capitato oggi:

twist

Flavio ha ragione; ma nel frattempo –sapete com’è– ecco Chubby!

Iscriviti

Ricevi al tuo indirizzo email tutti i nuovi post del sito.

Unisciti agli altri 84 follower