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.
fuente
(if (not (array 1)) 8 false) -> false
?(+ 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 volverfalse
? 4. La definición denot
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)
igual9
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)
Respuestas:
Haskell,
1370126311791128116311071084 bytes * 0.8 * 0.85 = 737.12Programa completo, lectura
stdin
y escritura astdout
.g
es la versión de la función, también.Implementa ambas funciones parciales y una evaluación perezosa de
if
.Ejecuciones de muestra (de la versión de la función):
Ahora tiene todas las pruebas unitarias de la descripción:
fuente
e[K,L _]
que podría usardrop 1
como una versión seguratail
y usartake
para una versión segura dehead
unir las dos definiciones dee[K,L _]
notElem
.otro consejo: puede hacerlos=string
y usarlo en lugar de ambosstring
ychar
(s"C"
vs.char 'C'
). otro consejo: use guardias en lugar deif
sMaybe
valores por listas.Nothing
es[]
yJust x
es[x]
. Esto se deshace de los constructores largos y añade algunas funcionalidades más:if p then Just x else Nothing
es[x|p]
,(==Nothing)
esnull
, la lista mónada se convierte en la misma que la mónada tal vez, y así sucesivamente.Python 2, 1417 * 0.8 * 0.85 = 963.56
Revisión completa. Si desea ver las versiones anteriores, consulte el historial de edición .
Hay mucho más por jugar al golf. Estoy trabajando lentamente en eso.
Con zlib / base64 obtenemos 1093 * 0.8 * 0.85 = 743.24 :
Nota: Si ves que mi puntaje sube, probablemente sea porque encontré algunos errores
fuente
Lisp común, 868 bytes * 0.85 = 737.8
¿Es una trampa implementar Lisp con Lisp? Mucho para optimizar aquí, todavía.
Imprime una E en caso de un error en la entrada. Ejecuciones de muestra:
fuente
Haskell, 972
Una solución bastante hacky. esto almacena todo como cadenas en forma lista para la salida (sus tipos se pueden distinguir por su primera letra)
0..9
para números,(
para listas,t
of
booleanos y todo lo demás para funciones.para ejecutar usa la
r
función.fuente