Scheme è un linguaggio di programmazione funzionale, un dialetto del Lisp di cui mantiene tutte le caratteristiche, che è stato sviluppato negli anni settanta da Guy L. Steele e Gerald Jay Sussman, che lo introdussero nel mondo accademico con una serie di articoli noti come le Lambda Papers e nel libro Structure and Interpretation of Computer Programs. Il desktop manager GNOME incorpora l'interprete Scheme Guile.

Programmazione A differenza della maggior parte degli altri linguaggi di programmazione, Scheme utilizza una notazione prefissa, ovvero una notazione in cui al posto di scrivere (2 + 3) si scrive (+ 2 3). Questa notazione si propaga a tutte le funzioni, sicché se abbiamo una funzione N-aria f, la sua rappresentazione sarà (f argomento1 argomento2... argomentoN).

Tipi di dato Lo Scheme implementa tutti i tipi di dato fondamentale: booleani, numeri, caratteri, stringhe, vettori. Tuttavia include anche tipi speciali, tra cui le liste (coppie), le porte (flussi di dati), i simboli e le procedure.

Per riconoscere questi tipi di dato, Scheme mette a disposizione delle particolari funzioni il cui identificatore ha il formato "tipoDiDato?" che restituiscono il booleano TRUE se l'argomento passato è nel formato tipoDiDato. Ecco una tabella riassuntiva:

FunzioneSpiegazione(boolean? argomento) l'argomento è un booleano?(number? argomento) l'argomento è un numero?(char? argomento) l'argomento è un carattere?(string? argomento) l'argomento è una stringa?(vector? argomento) l'argomento è un vettore?(list? argomento) l'argomento è una lista?(port? argomento) l'argomento è una porta?(symbol? argomento) l'argomento è un simbolo?(procedure? argomento) l'argomento è una procedura?

Vediamo ora alcuni dettagli su questi tipi di dati.

Numerici Le costanti numeriche si scrivono secondo le usuali regole. Sono disponibili vari tipi di dato numerico: numeri positivi e negativi, numeri con la virgola, numeri in notazione esponenziale e numeri complessi. Per riconoscere a che classe di numeri appartiene un'espressione numerale, è possibile usare una delle seguenti funzioni, nuovamente designate da un identificatore con il punto interrogativo, che restituiscono un valore booleano:

Funzione Risultato restituito(integer? argomento) l'argomento è un intero(rational? argomento) l'argomento è un numero razionale(real? argomento) l'argomento è un reale(complex? argomento) l'argomento è un numero complesso

È inoltre possibile specificare la base di numerazione tramite #fN, dove f è la base ed N un numero:

Basi possibiliTipo di base#bN N è binario#oN N è ottale#dN N è decimale#xN N è esadecimale

dN non si usa praticamente mai, visto che la base è di default quella decimale.

Esistono poi le seguenti funzioni booleane (punto interrogativo) e di conversione:

Funzioni funzionalità(exact? espressione) L'espressione restituisce un numero esatto?(inexact? espressione) L'espressione restituisce un numero approssimato?(exact->inexact espressione) Approssima il valore dell'espressione(inexact->exact espressione) Converte un valore approssimato in un valore esatto.

Per classificare ulteriormente i numeri, ad esempio tra positivi e negativi, o pari e dispari, Scheme mette a disposizione le seguenti primitive:

Funzioni funzionalità(zero? numero) Il numero è uno zero?(positive? numero) Il numero è positivo?(negative? numero) Il numero è negativo?(odd? numero) Il numero è dispari?(even? numero) Il numero è pari?

Ovviamente poi, come nella maggioranza dei linguaggi, si hanno a disposizione sia gli operatori aritmetici che molte funzioni matematiche.

Funzioni Risultato restituito tipo(+ arg1 arg2 ... argN) arg1+arg2+...+argN numerico(- arg1 arg2 ... argN) arg1-arg2-...-argN numerico(* arg1 arg2 ... argN) arg1*arg2*...*argN numerico(/ arg1 arg2 arg3... argN) (..(arg1/arg2)/arg3).../argN numerico(log arg) log naturale di arg numerico(exp arg) esponenziale di arg numerico(sin arg) seno di arg numerico(cos arg) coseno di arg numerico(tan arg) tangente di arg numerico(asin arg) arcoseno di arg numerico(acos arg) arcocoseno di arg numerico(atan arg) arcotangente di arg numerico(sqrt arg) radice quadrata arg numerico(expt base potenza) base^potenza numerico(abs arg) valore assoluto di arg numerico(quotient arg1 arg2)risultato intero della divisione arg1/arg2 numerico(remainder arg1 arg2) resto (positivo) della divisione numerico(modulo arg1 arg2) resto della divisione numerico(ceiling arg) tetto di arg (approssimazione della parte intera per eccesso) numerico(floor arg) pavimento di arg (approssimazione della parte intera per difetto) numerico(round arg) approssimazione generica di arg numerico(truncate arg) troncamento di arg numerico(max arg1 ... argN) valore massimo tra arg1 e argN numerico(min arg1 ... argN) valore minimo tra arg1 e argN numerico(gcd arg1 ... argN) massimo comune divisore tra arg1..e.. argN numerico(lcm arg1 ... argN) minimo comune multiplo tra arg1.. e.. argN numerico(numerator arg) numeratore di arg numerico(denominator arg) denominatore di arg numerico(= arg1 arg2 ... argN) arg1=arg2=...=argN? booleano(< arg1 arg2 ... argN) arg1<arg2<...<argN? booleano(> arg1 arg2 ... argN) arg1>arg2>...>argN? booleano(>= arg1 arg2 ... argN) arg1>=arg2>=...>=argN? booleano(<= arg1 arg2 ... argN) arg1<=arg2<=...<=argN? booleano

Caratteri In Scheme i caratteri si indicano con un cancelletto, seguito da un backslash ed il carattere che si vuole esprimere (senza il backslash, ci sarebbe ambiguità tra i caratteri T e F ed i booleani TRUE e FALSE).

Tra le funzioni rilevanti troviamo: Funzioni Risultato restituito(char=? char1 char2) char1 è uguale a char2?(char<? char1 char2) char1 è lessicograficamente inferiore a char2?(char>? char1 char2) char1 è lessicograficamente superiore a char2?(char<=? char1 char2) char1 è lessicograficamente non superiore a char2?(char>=? char1 char2) char1 è lessicograficamente non inferiore a char2?

Di queste funzioni esiste anche la versione ci, case insensitive: si ha quindi char-ci=?, char-ci<?, char-ci<=?, e così via.

Altre funzioni operanti sui caratteri sono le seguenti: Funzioni Risultato restituito tipo(char? arg) arg è un carattere? booleano(char-alphabetic? arg) arg è un carattere alfabetico? booleano(char-numeric? arg) arg è un carattere rappresentante un numero? booleano(char-whitespace? arg) arg è un carattere rappresentante uno spazio bianco? booleano(char-upper-case? arg) arg è un carattere alfabetico maiuscolo? booleano(char-lower-case? arg) arg è un carattere alfabetico minuscolo? booleano(char->integer arg) il carattere viene trasformato in numero numerico(integer->char arg) il numero viene trasformato in carattere carattere(char-upcase arg) il carattere alfabetico diventa maiuscolo carattere(char-downcase arg) il carattere alfabetico diventa minuscolo carattere

Stringhe

Le stringhe in scheme si rappresentano tra doppi apici (esempio: "questa è una stringa"). Alcune funzioni per la gestione delle stringhe, sono le seguenti:

Funzioni Risultato restituito tipo(make-string n) crea una stringa lunga n stringa(make-string n ch) crea una stringa lunga n, formata dal solo carattere ch stringa(string char1 char2 ..charN) crea una stringa formata dai caratteri char1, char2,... charN stringa(string-length str) restituisce la dimensione di str numerico(string-ref str idx) restituisce il carattere alla posizione idx, della stringa str carattere(substring str beg end) restituisce la porzione della stringa str contenuta tra i due indici beg ed end stringa(string-set! str idx ch) sostituisce con ch, il carattere alla posizione idx nella stringa str stringa(string-append str1 str2...strN) concatena le stringhe str1, str2, ..., strN stringa(string-copy str) restituisce una copia della stringa str stringa(string->list str) restituisce una lista formata dai caratteri della stringa str lista(list->string lst) restituisce una stringa formata dai caratteri della lista lst stringa

Come per i caratteri abbiamo degli operatori di confronto tra stringhe, che sono analoghi a quelli per i caratteri. I principali sono elencati in seguito, ed esistono anche nella versione string-ci (case insensitive): Funzioni Risultato restituito(string=? str1 str2) str1 è uguale a str2?(string<? str1 str2) str1 è lessicograficamente inferiore a str2?(string>? str1 str2) str1 è lessicograficamente superiore a str2?(string<=? str1 str2) str1 è lessicograficamente non superiore a str2?(string>=? str1 str2) str1 è lessicograficamente non inferiore a str2?

Liste Come specificato prima, le liste sono un particolare tipo di dato. Come gli array, rappresentano collezioni ordinate di elementi; a differenza degli array, gli elementi possono essere eterogenei (di tipi differenti fra loro), e inoltre non possono essere indicizzati.

Le liste sono realizzate come coppie: (2 3) rappresenta un esempio di lista che è evidentemente una coppia; ma anche (2 3 4) è in realtà una coppia, formata dal primo elemento (2) e da tutti gli altri (3 4). A sua volta, l'elemento "tutti gli altri" è una coppia formata dal suo primo elemento (3) e da tutti gli altri (in questo caso solo il 4). La lista è quindi descritta in modo molto naturale in termini ricorsivi.

Una lista può contenere qualunque tipo di dato, come caratteri, stringhe, booleani, e anche altre liste (esempio:"(2 3 (4 5))"); come già detto, possono anche contenere tipi di dato misti (esempio:"(#\T 4 (4 #F ("stringa")))").

Le due funzioni fondamentali per agire sulle liste si rifanno alla definizione ricorsiva accennata sopra: abbiamo così la funzione car, che restituisce il primo elemento, e cdr, che restituisce il secondo elemento (l'elemento "tutti gli altri").

Le principali funzioni che Scheme fornisce per le liste sono le seguenti:

Funzioni Risultato restituito tipo(list arg1 arg2 ... argN) costruisce una lista formata dagli argomenti arg1,arg2,...,argN lista(pair? lst) la lista lst è non vuota? booleano(car lst) restituisce il primo elemento della lista lst misto(cdr lst) restituisce la lista formata dagli elementi dal secondo all'ultimo elemento della lista lst lista(cadr lst) restituisce il primo elemento della lista che si otterrebbe invocando (cdr lst) misto(cons arg lst) restituisce una lista in cui al primo posto c'è arg lista(append lst1 lst2) concatena le due liste lst1 e lst2 lista(length lst) restituisce il numero di elementi della lista numerico(reverse lst) inverte gli elementi della lista lista Notiamo che in questo elenco sono disponibili funzioni aggregate che corrispondono alla composizione di car e cdr: l'espressione (cdr(cdr lst)) può essere scritta in modo più sintetico come (cddr lst), mentre (car (cdr lst)) si può sintetizzare con (cadr lst).

Scheme presenta poi altre funzioni composte, alcune delle quali simulano le funzioni sui vettori, ed altre la conversione lista a vettore e viceversa:

Funzioni Risultato restituito tipo(null? lst) la lista lst è vuota? booleano(set-car! lst arg) setta la prima posizione al valore arg lista(set-cdr! lst arg) setta la seconda posizione al valore arg lista(list-ref lst k) restituisce l'elemento nella posizione k della lista lst misto(list->vector lst) restituisce un vettore formato dagli elementi della lista lst vettore(vector->list vctr) restituisce una lista formata dagli elementi vel vettore vctr vettore

Funzioni particolari che agiscono sulle liste sono le funzioni apply, map e for-each:

Funzioni Risultato restituito(apply f lst) esegue la funzione f usando come elementi quelli presenti nella lista(map f lst1...lstN) esegue la funzione f sugli elementi allo stesso indice della lista(for-each f lst1...lstN) esegue la funzione f sugli elementi delle liste

Per spiegare meglio, facciamo un esempio: > (define lst1 (list 2 3 4 5 6)) > (apply + lst1) > > 20 > (define lst2 (list 1 2 3 4 5)) > (map + lst1 lst2) > > (3 5 7 9 11)

Vettori