Scheme

llenguatge de programació

El llenguatge de programació Scheme és un llenguatge funcional (si bé "impur", ja que, per exemple, les seves estructures de dades no són immutables) i un dialecte de Lisp. Fou desenvolupat per Guy L. Steele i Gerald Jay Sussman en la dècada dels setanta i introduït en el món acadèmic a través d'una sèrie d'articles coneguts com el Lambda Papers de Sussman i Steele.

Infotaula de llenguatge de programacióScheme
Tipusllenguatge de programació, llenguatge de programació multiparadigma, llenguatge de programació funcional, llenguatge de programació procedural i metaprogramming language (en) Tradueix Modifica el valor a Wikidata
Data de creació1975 Modifica el valor a Wikidata
DissenyGuy L. Steele i Gerald Jay Sussman Modifica el valor a Wikidata
Paradigma de programacióprogramació funcional, programació procedimental, metaprogramming (en) Tradueix i llenguatge imperatiu Modifica el valor a Wikidata
Darrera versió estableR7RS-small () Modifica el valor a Wikidata
Dialecte deLisp Modifica el valor a Wikidata
Influenciat perLisp, ALGOL i MDL Modifica el valor a Wikidata
Extensió dels fitxersscm i ss Modifica el valor a Wikidata
Pàgina webscheme-reports.org Modifica el valor a Wikidata

La filosofia de Scheme és decididament minimalista. El se objectiu no és acumular un gran nombre de funcionalitats, sinó d'evitar les debilitats y restriccions que fan necessària la seva addició. Així, Scheme proporciona el mínim nombre possible de nocions primitives, construït la resta basant-se en aquest reduït nombre d'abstraccions. Per exemple, el mecanisme principal pel control de flux són les crides recursives finals.

Scheme fou el primer dialecte de Lisp que utilitzà àmbit estàtic o lèxic (en lloc de dinàmic) de forma exclusiva. També fou un dels primers llenguatges de programació amb continuacions explícites. Scheme ofereix també gestió automàtica de memòria (recol·lecció de brossa).

Les llistes són l'estructura de dades bàsica del llenguatge, que també ofereix "arrays" entre els seus tipus predefinits. degut a la seva especificació minimalista, no hi ha sintaxi explícita per a crear registres o estructures, o per programació orientada a objectes, però moltes implementacions ofereixen les esmentades funcionalitats

Scheme, originalment, s'anomenava "Schemer", continuant la tradició dels llenguatges Planner i Conniver. El seu nom actual és a causa del fet que els seus autors utilitzaven el sistema operatiu ITS, que limitava la longitud dels noms de fitxers a 6 caràcters

Avantatges de Scheme

modifica

Scheme, com tots els dialectes de Lisp, té una sintaxi molt reduïda, comparada amb la d'altres llenguatges. No necessita regles de precedència, ja que, utilitza sempre la posició prefix tant per crides a funció com per a operadors.

Les macros de Scheme permeten adaptar-lo a qualsevol domini. Poden ser utilitzades, per exemple per afegir suport a la programació orientada a objectes. Scheme proporciona un sistema de macros "higiènic" que, encara que no tant potent com el de Common Lisp, és molt més segur i, amb freqüència, senzill d'utilitzar. L'avantatge d'un sistema de macros d'aquest tipus (que també es troba en altres llenguatges com Dylan) és que evita automàticament col·lisions entre els noms utilitzats en la definició de la macro i en el context en aquesta s'expandeix. En contrapartida, les macros higièniques no poden introduir nous símbols.

Scheme facilita la programació funcional. La programació funcional pura no requereix variables globals ni pateix d'efectes secundaris, i és, per tant, automàticament segura en presència de processos concurrents (thread-safe), a mercè de facilitar considerablement la verificació de programes, almenys en comparació amb l'estil imperatiu.

En Scheme, els procediments són objectes de primera classe. Aquest fet permet la definició de funcions d'ordre superior, que faciliten un major grau d'abstracció en els programes. També és possible la creació de procediments anònims.

L'estàndard de Scheme és també minimalista. Això comporta avantatges i inconvenients. Per exemple, escriure un compilador o intèrpret de Scheme que sigui fidel a l'estàndard és més fàcil que implementar un de Common Lisp; Fer servir Lisp en dispositius amb poca memòria serà també més factible si utilitzem Scheme en lloc de Common Lisp. Als afeccionats a Scheme els diverteix molt assenyalar que el seu estàndard, amb només 50 pàgines, és més curt que l'índex del llibre de Guy Steele Common Lisp: The Language!

Desavantatges de Scheme

modifica

L'estàndard de Scheme és realment minimalista, i especifica únicament el llenguatge en si mateix. Això provoca l'existència de multitud d'implementacions diferents, cadascuna de les quals introdueix extensions y biblioteques pròpies que les fa incompatibles entre elles. Els Scheme Requests for Implementation (SRFI) tracten de posar remei a aquest problema.

Hi ha gent pressuposa que els procediments i variables comparteixen el mateix espai de nombres com un desavantatge, ja que algunes funcions tenen noms que són d'ús comú per variables. Per exemple, list és el nom d'un procediment, de manera que és molt habitual veure lst o lyst como noms de variables, en lloc de l'obvi (per als anglosaxons!) "list".

Com hem dit, l'espai de noms és únic (en argot tècnic, Scheme és el que es coneix com un LISP-1) i, per això, també inclou a les macros. Això impossibilita distingir l'ús d'una macro del d'una funció, així que, si no consultem la definició de cadascun dels objectes utilitzats, no serà en general possible determinar l'ordre devaluació en programes amb els que no estiguem familiaritzats.

Estàndards

modifica

Existeixen dos estàndards que defineixen el llenguaje de programació Scheme: l'estàndard oficial de la IEEE, i un altre estàndard conegut como Revisedn-th Report on the Algorithmic Language Scheme, quasi sempre abreviat como RnRS, on n és el nombre de la revisió. L'últim RnRS és R5RS, i està displonible en línia.

Elements del llenguatge

modifica

Comentaris

modifica

Els comentaris s'inicien amb un punt i coma (;) i continuen fins al final de la línia.

Variables

modifica

El tipatge de les variables és dinàmic. Per associar-les a un valor concret, podem utilitzar o bé define, o bé una expressió let, o bé alguna de les seves variants. Les variables assignades en el primer nivell utilitzen define estan en àmbit global (és a dir, són visibles en la resta del programa).

 (define var1 value)

Les variables assignades mitjançant let només són visibles al cos de l'esmentat let:

 (let ((var1 value))
 ...
 àmbit de var1
 ...)

Procediments

modifica

Les funcions o procediments són objectes de primera classe en Scheme. Poden ser assignats a variables. Per exemple, una funció de dos arguments arg1 y arg2 poden definir-se com

 (define fun
 (lambda (arg1 arg2)
 ...))

o en la forma abreviada equivalent:

 (define (fun arg1 arg2)
 ...)

Les crides a funció tenen la sintaxi següent:

 (fun value1 value2)

Como veiem, la funció invocada es troba en primer lloc, seguida dels arguments de la crida, formant una llista. Podem també utilitzar el procediment apply, que pren dos arguments: el primer és el procediment que volem invocar, mentre que el segon és la llista d'arguments. Així, l'anterior crida a una funció pot escriure's, de forma equivalent, com

 (apply fun (list value1 value2))

En Scheme, les funciones es divideixen, bàsicament, en dos categories: els procediments definits per l'usuari i les primitives. Les primitives estan predefinides en el llenguatge, i inclouent +, -, *, /, set!, car, cdr, i altres procediments bàsics. Moltes implementacions permeten a l'usuari redefinir algunes primitives. Por exemple, el següent codi

 (define +
 (lambda (x y)
 (- x y)))

converteixen la primitiva + en un procediment definit per l'usuari que resta els seus dos arguments en lloc de sumar-los.

Llistes

modifica

Scheme utilitza llistes enllaçades de forma anàloga a altres dialectes de Lisp.

Tipus de dades

modifica

Altres tipus de dades en Scheme són els enters, racionals, reals, complexes, símbols, cadenas, i ports, llistes associatives, taules hash, vectors, arrays y estructures.


La majoria d'implementacions proporciona allò que es coneix com una torre numèrica completa, així com aritmètica exacta e inexacta.

Els valors booleans es representen mitjançant els símbols #t y #f. En realitat, qualsevol valor distint de #f (incloent la llista buida) s'interpreta com 'vertader' en un context adequat, mentre que en altres dialectes de Lisp la llista buida és interpretada com el valor booleà fals.

Els símbols poden ser definits de diverses maneres, sent

 'symbol
 (string->symbol "symbol")

les més comunes.

Igualtat

modifica

Schemem té tres tipus diferents d'igualtat:

eq?
Retorna #t si els dos objectes són exactament el mateix objecte, comprovant també on estan emmagatzemats físicament.
eqv?
Normalment igual que eq?, però tracta alguns objectes (per exemplo, caràcters y nombres) de forma especial perquè els nombres que siguin iguals fossin eqv? encara que fossin eq?.
equal?
Compara el contingut de les estructures de dades tals com llistes, vectors y cadenes per determinar si són iguals.

Les operadores d'equivalència dependents del tipus també existeixen en Scheme:

string=?
Compara dues cadenes
char=?
Compara dos caràcters
=
Compara nombres

Estructures de control

modifica

Avaluació condicional

modifica
 (cond (prova1 expr1)
 (prova2 expr2)
 ...
 (else exprn))

La primera expressió per la que la prova fos ser certa (qualsevol cosa excepte #f és pres com a cert) serà avaluada. Si totes les prove resulten ser #f, s'avalua la clàusula else.

Una variant de la clàusula cond és

 (cond ...
 (test => expr)
 ...)

En aquest cas, expr ha de resultar en una funció que pren un solo argument. Si test resulta ser cirt, es crida a la funció anterior amb el valor retornat per test.

Scheme també té

 (if test then-expr else-expr)

però s'utilitza molt menys porque cond és més general i normalment resulta més llegible.

Els bucles en Scheme solen prendre la forma d'una recursió final. Un exemple clàssic és la funció factorial, que pot definir-se sense recursió final com:

 (define (factorial n)
 (cond ((= n 0) 1)
 (else (* n (factorial (- n 1))))))

 (factorial 5)
 ;; => 120

o una funció d'ordre superior como map, que aplica una funció a cada element d'una llista, pot també definir-se sense recursió final de la següent forma:

 (define (map f lst)
 (cond ((null? lst) lst)
 (else (cons (f (car lst))
 (map f (cdr lst))))))

 (map (lambda (x) (* x x)) '(1 2 3 4))
 ;; => (1 4 9 16)

Podems definir ambdues fent servir la recursió final como segueix. L'expressió let amb nom y la sentència do son sucre sintàctic que simplifica les definicions amb recursió final.

 (define (factorial n)
 (let loop ((fact 1)
 (n n))
 (cond ((= n 0) fact)
 (else (loop (* n fact) (- n 1))))))

 (factorial 5)
 ;; => 120


 (define (map f lst)
 (do ((lst lst (cdr lst))
 (res '() (cons (f (car lst)) res)))
 ((null? lst) (reverse res))))

 (map (lambda (x) (* x x)) '(1 2 3 4))
 ;; => (1 4 9 16)

Fixa't que en ambdós casos es prefereix la versió amb recursió final degut al seu menor ús d'espai.

Entrada/sortida

modifica

Scheme té el concepte de ports d'on llegir o als que escriure. Scheme defineix tres ports per defecte, accessible amb les funcions current-input-port, current-output-port y current-error-port.

Hola Món

modifica
 (let ((hola-mon
 (lambda ()
 (display "Hola, món") 
 (newline))))
 (hola-mon))

o, simplement

 (display "Hola, món\n")