Desafío
Su desafío es diseñar un intérprete para un lenguaje similar al lisp, que a partir de ahora será acuñado: GLisp . El código del programa para GLisp consistirá en una cantidad arbitraria de expresiones anidadas indicadas entre paréntesis, en la siguiente forma:
(func arg1 arg2 ...)
Tenga en cuenta que el intérprete debe permitir espacios en blanco extraños antes y después de corchetes, funciones y argumentos.
Tipos
Implementará cuatro tipos, Integer, List, Boolean y Function. Los valores enteros y booleanos se pueden insertar explícitamente en el código fuente con su propia sintaxis. Su intérprete debe suponer que una serie de caracteres numéricos denota un entero (no es necesario que implemente una sintaxis para insertar explícitamente enteros negativos). Su intérprete también debe asumir eso true
y false
se designan valores booleanos. Las funciones no pueden ser definidas explícitamente por el usuario, y siempre devolverán un valor único (una Lista de cualquier longitud cuenta como un valor único).
Las funciones
Se requieren las siguientes funciones para ser implementadas, y están en el formato Función , Arity . Si un Arity n
es procesado por un signo más, entonces eso denota n
o más argumentos. Puede suponer que todos los argumentos dados a una función son del mismo tipo, a menos que se especifique lo contrario. También puede suponer que si no se especifica ningún comportamiento para un tipo certian, puede suponer que ningún argumento de esa función será nunca de ese tipo. Se hará referencia a los argumentos en el siguiente diagrama:
(func argument1 argument2 ... argumentn)
+ , 2+
- Si todos los argumentos son del tipo Integer , debe devolver la suma de los argumentos
- Si todos los argumentos son del tipo Lista , debe devolver la concatenación de los argumentos en orden ascendente (
arg1+arg2+ ...
) - Si todos los argumentos son del tipo booleano , debe devolver la secuencia lógica de todos los argumentos
(+ 1 2 3 4 5) -> 15
(+ (list 1 2) (list 3 4)) -> (list 1 2 3 4)
(+ true true true) -> true
- , 2+
- Si todos los argumentos son del tipo Integer , debe devolver la diferencia de los argumentos (
arg1-arg2- ...
) - Si todos los argumentos son del tipo booleano , debe devolver el lógico Cualquiera de la secuencia de argumentos
(- 8 4 3) -> 1
(- 0 123) -> -123
(- true false false true false) -> true
- Si todos los argumentos son del tipo Integer , debe devolver la diferencia de los argumentos (
* , 2+
- Si todos los argumentos son de tipo Integer , debe devolver el producto de los argumentos
- Si un argumento es de tipo Lista y el otro es de tipo Entero (puede suponer que estos serán los únicos argumentos dados), debe devolver una nueva Lista con los elementos en
arg1
repetidasarg2
ocasiones. (* 1 2 3 4 5) -> 120
(* (list 1 2 3) 2) -> (list 1 2 3 1 2 3)
/ , 2+
- Si todos los argumentos son del tipo Integer , debe devolver el cociente de los argumentos (
arg/arg2/ ...
) (puede suponer que la división se realiza secuencialmente y que la porción decimal en cada paso se trunca) - Si un argumento es de tipo Lista y el otro es de tipo Función , debe devolver la Lista resultante después de que
arg2
se haya asignado sobre cada valor (/ 100 10 3) -> 3
(/ (list 1 2 3) inc) -> (list 2 3 4)
- Si todos los argumentos son del tipo Integer , debe devolver el cociente de los argumentos (
% , 2
- Si todos los argumentos son del tipo Entero , debe devolver el módulo de los argumentos
(% 4 2) -> 0
= , 2+
- Si tanto el tipo como el valor de todos los argumentos son iguales, debe devolver verdadero. De lo contrario, devuelve falso.
(= 0 0 0) -> true
(= 0 false (list)) -> false
lista , 0+
- Debe devolver una lista de todos los argumentos, independientemente del tipo. Si no se dan argumentos, debe devolver una lista vacía
(list 3 4 (list 5)) -> (list 3 4 (list 5))
inc , 1
- Si el argumento es de tipo Entero , debe devolver el Entero incrementado en uno
- Si el argumento es de tipo Lista , debe devolver la Lista girada en sentido horario una sola rotación
(inc 1) -> 2
(inc (list 1 2 3)) -> (list 3 1 2)
dic , 1
- Si el argumento es de tipo Entero , debe devolver el Entero decrementado en uno
- Si el argumento es de tipo Lista , debe devolver la Lista girada en sentido antihorario una sola rotación
(dec 1) -> 0
(dec (list 1 2 3)) -> (list 2 3 1)
si , 3
- Si se le dan tres argumentos de cualquier tipo: si el valor de verdad de
arg1
es verdadero, devuelvearg2
, de lo contrario devuelvearg3
(if (not (list 1)) 8 false) -> false
- Si se le dan tres argumentos de cualquier tipo: si el valor de verdad de
no 1
- Si se le da un argumento de cualquier tipo, si el valor de verdad de
arg1
es False, returntrue
, else returnfalse
. (not (list)) -> true
- Si se le da un argumento de cualquier tipo, si el valor de verdad de
len , 1
- Si se le da un argumento de tipo Lista , devuelve la longitud de
arg1
(len (list 4 2 true (list 3) (list))) -> 5
- Si se le da un argumento de tipo Lista , devuelve la longitud de
Tabla de verdad:
0, (list), false -> false
donde (list)
denota una lista vacía. Todo lo demás es true
.
Su intérprete puede ser un programa completo que lee la entrada de origen de stdin o un archivo, o una función que toma el origen como una cadena y devuelve el valor de salida.
Si elige el primero, la salida para números enteros es simplemente números, para booleanos es true
o false
, y para listas es una secuencia de valores separados por espacios entre corchetes (por ejemplo, (1 2 3 4 (5 6 7))
denota (list 1 2 3 4 (list 5 6 7))
).
Si elige este último, el valor debe devolverse en el tipo correspondiente del lenguaje de implementación o, si no existe un tipo similar, un tipo personalizado. Las listas se pueden devolver como matrices o vectores si el idioma no tiene un tipo de lista , booleanos deben ser devueltos como un tipo booleano en el lenguaje, o un tipo personalizado si el idioma no es compatible con ellos.
Casos de prueba
(list 1 2 3 (list 4 5 true)) -> (1 2 3 (4 5 true))
(/ 4000 (+ 1 2 3 4 (* 5 8))) -> 80
(+ (not (- (len (list 5 6 7)) (/ 10 3))) true) -> true
(if ( len (list ) ) 4 (if (+ (= 8 8 8) (not (list 4))) 8 5)) -> 5
Aclaraciones
- Su intérprete puede lidiar con entradas no válidas de cualquier manera que elija, pero no debe lanzar una excepción (sin embargo, puede imprimir un mensaje de error y salir sin problemas)
- Las funciones siempre evaluarán los argumentos de izquierda a derecha
- La entrada inválida es cualquier entrada que sea sintácticamente incorrecta. Esto incluye, entre otros, corchetes no coincidentes, división por cero y funciones parcialmente aplicadas (a menos que vaya por la bonificación)
- Para
=
, si alguno de los valores es diferente o alguno de los tipos es diferente, regresefalse
Bonos
- Puntuación * 0.8 si admite funciones parcialmente aplicadas. Por ejemplo,
((+ 2) 3)
sería lo mismo que(+ 2 3)
, pero permite cosas como(/ (list 1 2 3) (+ 2))
. Puede suponer que una función se aplica parcialmente si recibe menos de su número mínimo de argumentos - Puntuación * 0.85 si no evalúa los argumentos aplicados a
if
menos que se vayan a devolver
Este es el código de golf, por lo que gana el intérprete con el conteo de bytes más bajo.
(+ 3 (if false 5))
? En términos generales, ¿qué es realmente "no devolver nada"? No especificó ningún tipo de unidad para volver a
(+ bool bool...)
lógico AND y (- bool bool...)
lógico OR? La notación de anillo estándar se usaría +
para OR y *
para AND. 2. ¿Se pretende que la "entrada no válida" cubra casos como los (/ 2 0)
que son sintácticamente correctos? 3. Para =
, si los valores no son todos iguales, ¿debería volver false
? 4. La definición de not
parece ser al revés. 5. ¿Qué son los tokens? Usted dice que el intérprete debe manejar espacios en blanco adicionales, pero no dice en qué espacio en blanco puede confiar. Para preguntas complejas como esta, realmente debería usar el sandbox para que se pueda verificar la especificación.
((+ 2 3) 4)
igual 9
o un error? En particular, para las funciones var-arg, no está claro cuándo se debe considerar la aplicación parcial. Se vuelve aún más turbio con cosas como((if true (+ 2 3) (- 5)) 4)
(if (not (array 1)) 8 false) -> false
?