Scheme
Scheme | ||
---|---|---|
![]() | ||
? | ||
Información general | ||
Extensiones comunes | scm y ss | |
Paradigma | multi-paradigma | |
Apareció en | 1975 | |
Diseñado por | Guy L. Steele y Gerald Jay Sussman | |
Sistema de tipos | Fuerte, dinámico | |
Implementaciones | Scheme, Scheme 48, Chicken, Gambit, FLUENT,Guile,Bigloo, Chez Scheme, STk, STklos, Larceny, SCM | |
Dialectos | T | |
Influido por | Lisp | |
Schemees unlenguaje de programaciónfuncional(si bienimpuropues sus estructuras de datos no son inmutables) y undialectodeLisp.Fue desarrollado porGuy L. SteeleyGerald Jay Sussmanen la década de los setenta e introducido en el mundo académico a través de una serie de artículos conocidos como losLambda Papersde Sussman y Steele.
La filosofía de Scheme esminimalista.Su objetivo no es acumular un gran número de funcionalidades, sino evitar las debilidades y restricciones que hacen necesaria su adición. Así, Scheme proporciona el mínimo número posible de nociones primitivas, construyendo todo lo demás a partir de un reducido número de abstracciones. Por ejemplo, el mecanismo principal para el control de flujo son lasllamadas recursivas.
Scheme fue el primer dialecto de Lisp que usóámbito estático,también conocido como ámbito léxico, (en lugar dedinámico) de forma exclusiva. También fue uno de los primeros lenguajes de programación concontinuacionesexplícitas, un mecanismo para guardar y usar el estado entero de un programa en un momento determinado. Scheme ofrece también gestión automática de memoria (recolección de basura).
Las listas son la estructura de datos básica del lenguaje, que también ofrecearraysentre sus tipos predefinidos. Debido a su especificación minimalista, no hay sintaxis explícita para crear registros o estructuras, o paraprogramación orientada a objetos,pero muchas implementaciones ofrecen dichas funcionalidades.
Scheme se llamaba originalmente «Schemer», siguiendo la tradición de los lenguajesPlanneryConniver.Su nombre actual se debe a que sus autores usaban elsistema operativo ITS,que limitaba la longitud de los nombres de fichero a seis caracteres.
Ventajas de Scheme
[editar]Scheme, como todos los dialectos deLisp,tiene una sintaxis muy reducida comparado con muchos otros lenguajes. No necesitareglas de precedenciaen su gramática, ya que usanotación prefijapara todas las llamadas a función. En el mundo de Lisp tales expresiones son conocidas comoS-expressions.
El poder característico de los Lisp reside en la simpleza de su sintaxishomoicónicahecha de listas anidadas, que refleja la estructura delárbol de sintaxis abstractadel programa y lo pone a disposición del programador. Esto facilita lametaprogramaciónmediante macros. Lasmacrosde Scheme permiten adaptarlo a nuevos dominios con facilidad, por ejemplo, para añadir soporte a laprogramación orientada a objetos.Existe por ejemplo una extensión que implementa un sistema de objetos escrito en menos de mil líneas de código de Scheme, incluyendo comentarios.[1] Scheme proporciona un sistema de macroshigiénicoque, aunque no tan potente como el deCommon Lisp,es mucho más seguro y, con frecuencia, sencillo de utilizar. La ventaja de un sistema de macros de este tipo (que también se encuentra en otros lenguajes, comoDylan) es que evita automáticamente colisiones entre los nombres usados en la definición de la macro y en el contexto en que ésta se expande. En contrapartida, las macros higiénicas no pueden introducir nuevos símbolos.
Scheme facilita laprogramación funcional.Aunque Scheme es impuro porque permite la asignación destructiva, usarlo al estilo de laprogramación funcional puraevita variables globales y sufrir deefectos secundarios,haciéndolo más seguro en presencia de procesos concurrentes (thread-safe), amén de facilitar considerablemente laverificación de programas,al menos en comparación con elestilo imperativo.
En Scheme, los procedimientos o funciones sonobjetos de primera clase.Ello permite la definición defunciones de orden superior;funciones que reciben o devuelven otras funciones, que facilitan un mayor grado de abstracción en los programas. También es posible la creación de procedimientos anónimos (literales) mediante el primitivo(lambda (arg1...) (...))
.
El estándar de Scheme es también minimalista. Ello conlleva ventajas e inconvenientes. Por ejemplo, escribir uncompiladorointérpretede Scheme que sea fiel al estándar es más fácil que implementar uno de Common Lisp; empotrarLispen dispositivos con poca memoria será también más factible si usamos Scheme en lugar de Common Lisp. A los aficionados a Scheme les divierte mucho señalar que su estándar, con sólo 50 páginas, es más corto que el índice del libro deGuy SteeleCommon Lisp: The Language.
Desventajas de Scheme
[editar]El estándar de Scheme es realmente minimalista y específico en sí. Ello provoca que existan multitud de implementaciones diferentes, cada una de las cuales introduce extensiones y bibliotecas propias que las hace incompatibles entre sí. LosScheme Requests for Implementation(SRFI) tratan de poner remedio a este problema.
Hay quien ve el hecho de que los procedimientos y variables compartan el mismo espacio de nombres como una desventaja, ya que algunas funciones tienen nombres que son de uso común para variables. Por ejemplo,list
es el nombre de un procedimiento, así que es muy habitual verlst
oxs
como nombres de variables, en lugar de «list
».
Como hemos dicho, el espacio de nombres es único (Scheme es un LISP-1) y, por tanto, también incluye a las macros. Ello hace imposible distinguir el uso de una macro del de una función, así que, si no consultamos la definición de cada uno de los objetos usados, no será en general posible determinar el orden de evaluación en programas con los que no estemos familiarizados.
Estándares
[editar]Existen dos estándares que definen el lenguaje de programación Scheme: elestándar oficialde laIEEE,y un estándar de facto conocido comoRevisedn-thReport on the Algorithmic Language Scheme,casi siempre abreviado como RnRS, dondenes el número de la revisión. El último RnRS es R6RS, y está displonibleen línea.
Elementos del lenguaje
[editar]Comentarios
[editar]Para agregar un comentario en Scheme se inicia con unpunto y coma(;
) y continúan hasta el final de la línea.
Variables
[editar]Las variables son dinámicamente tipadas. Para asociarlas a un valor concreto, podemos usardefine
,una expresiónlet
,o alguna de sus variantes. Las variables asignadas en el primer nivel usandodefine
están en ámbito global (es decir, son visibles en el resto de programa).
(definevar1value)
Las variables asignadas mediantelet
ven su ámbito reducido al cuerpo de dicholet
:
(let((var1value))
...
ámbitodevar1
...)
Procedimientos
[editar]Las funciones o procedimientos son objetos de primera clase en Scheme. Pueden ser asignados a variables. Por ejemplo, una función de dos argumentosarg1yarg2puede definirse como:
(definefun
(lambda(arg1arg2)
...))
o en la forma abreviada equivalente:
(define(funarg1arg2)
...)
Las llamadas a función tienen la sintaxis siguiente:
(funvalue1value2)
Como vemos, la función invocada se encuentra en primer lugar, seguida de los argumentos de la llamada, formando una lista. Podemos también utilizar el procedimientoapply
,que toma dos argumentos: el primero es el procedimiento que queremos invocar, mientras que el segundo es la lista de argumentos. Así, la anterior llamada a función puede escribirse, de forma equivalente, como:
(applyfun(listvalue1value2))
En Scheme, las funciones se dividen, básicamente, en dos categorías: los procedimientos definidos por el usuario y las primitivas. Las primitivas están pre-definidas en el lenguaje, e incluyen+
,-
,*
,/
,set!
,car
,cdr
,y otros procedimientos básicos. Muchas implementaciones permiten al usuario redefinir algunas primitivas. Por ejemplo, el siguiente código:
(define+
(lambda(xy)
(-xy)))
convierte la primitiva+
en un procedimiento definido por el usuario que resta sus dos argumentos en lugar de sumarlos.
Listas
[editar]Scheme usalistas enlazadasde forma análoga a otros dialectos deLisp.
Téngase en cuenta que la utilización de listas es mucho más sencilla que en otros lenguajes de programación tales como C, C++ y Pascal.
Tipos de datos
[editar]Otros tipos de datos en Scheme sonlos enteros,racionales,reales,complejos,símbolos,cadenas,ypuertos,listas asociativas,tablas hash,vectores,arraysyestructuras.
La mayoría de implementaciones proporciona lo que se conoce como unatorre numéricacompleta, así como aritméticaexactaeinexacta.
Los valores booleanos se representan mediante los símbolos#t
y#f
.En realidad, cualquier valor distinto de#f
(incluyendo la lista vacía) se interpreta como 'verdadero' en un contexto adecuado, mientras que en otros dialectos de Lisp la lista vacía es interpretada como el valor booleano falso.
Los símbolos pueden ser definidos de varias maneras, siendo
'symbol
(string->symbol"symbol")
las más comunes.
Igualdad
[editar]Scheme tiene tres tipos diferentes de igualdad:
eq?
- Devuelve
#t
si los dos objetos son exactamente el mismo objeto, comprobando incluso dónde están guardados físicamente. eqv?
- Normalmente igual que
eq?
,pero trata algunos objetos (por ejemplo, caracteres y números) de forma especial para que los números que sean iguales seaneqv?
incluso si no soneq?
. equal?
- Compara el contenido de las estructuras de datos tales como listas, vectores y cadenas para determinar si son iguales.
También existen en Scheme los operadores de equivalencia dependientes del tipo:
string=?
- Compara dos cadenas
char=?
- Compara dos caracteres
=
- Compara números
Estructuras de control
[editar]Evaluación condicional
[editar](cond(prueba1expr1)
(prueba2expr2)
...
(elseexprn))
La primera expresión para la que la prueba resulte ser cierto (cualquier cosa salvo#f
cuenta como cierto) será evaluada. Si todas las pruebas resultan ser#f
,se evalúa la cláusulaelse
.
Una variante de la cláusulacond
es:
(cond...
(test=>expr)
...)
En este caso,expr
debe resultar en una función que toma un solo argumento. Sitest
resulta ser cierto, se llama a la función anterior con el valor devuelto portest
.
Scheme también tiene:
(iftestthen-exprelse-expr)
pero se usa mucho menos porquecond
es más general y normalmente resulta más legible.
Bucles
[editar]Los bucles en Scheme suelen tomar la forma de una recursión final otail recursionen inglés. Este tipo de recursión es preferido porque dispensa la acumulación de tramas en la pila de llamadas y su subsecuente desbordamiento. El estándar exige a las implementaciones optimizar llamadas en posición de recursión final para generar código equivalente a un ciclo en lenguajes imperativos. Un ejemplo clásico es la función factorial, que puede definirse sin recursión final como:
(define(factorialn)
(cond((=n0)1)
(else(*n(factorial(-n1))))))
(factorial5)
;; => 120
O con recursión final usando un procedimiento extra:
(define(factorialn)
(fact-iter1n))
(define(fact-iterproductn)
(if(<n2)
product
(fact-iter(*productn)
(-n1))))
(factorial5)
;; => 120
Otra forma típica de bucle es una función de orden superior comomap
,que aplica una función a cada elemento de una lista, puede también definirse sin recursión final de la siguiente forma:
(define(mapflst)
(cond((null?lst)lst)
(else(cons(f(carlst))
(mapf(cdrlst))))))
(map(lambda(x)(*xx))'(1234))
;; => (1 4 9 16)
Podemos definir ambas usando la recursión final como sigue. La expresiónlet
con nombrey la sentenciado
sonazúcar sintácticaque simplifica las definiciones con recursión final.
(define(factorialn)
(letloop((fact1)
(nn))
(cond((=n0)fact)
(else(loop(*nfact)(-n1))))))
(factorial5)
;; => 120
(define(mapflst)
(do((lstlst(cdrlst))
(res'()(cons(f(carlst))res)))
((null?lst)(reverseres))))
(map(lambda(x)(*xx))'(1234))
;; => (1 4 9 16)
Adviértase que en ambos casos se prefiere la versión con recursión final debido a su menor uso de espacio.
Entrada/salida
[editar]Scheme tiene el concepto depuertosde donde leer o a los que escribir. Scheme define tres puertos por defecto, accesibles con las funcionescurrent-input-port
,current-output-port
ycurrent-error-port
.