Desafio
Seu desafio é projetar um intérprete para uma linguagem parecida com lisp, que a partir de agora será cunhada: GLisp . O código do programa para GLisp consistirá em uma quantidade arbitrária de expressões aninhadas denotadas por colchetes, da seguinte forma:
(func arg1 arg2 ...)
Observe que o intérprete deve permitir caracteres de espaço em branco estranhos antes e depois de colchetes, funções e argumentos.
Tipos
Você implementará quatro tipos, Inteiro, Lista, Booleano e Função. Valores inteiros e booleanos podem ser explicitamente inseridos no código-fonte com sua própria sintaxe. Seu intérprete deve assumir que uma sequência de caracteres numéricos indica um número inteiro (você não precisa implementar uma sintaxe para inserir explicitamente números inteiros negativos). Seu intérprete também deve assumir que true
e false
são designados valores booleanos. As funções não podem ser definidas explicitamente pelo usuário e sempre retornam um único valor (uma lista de qualquer comprimento conta como um valor único).
Funções
As seguintes funções precisam ser implementadas e estão no formato Função , Arity . Se uma Arity n
é seguida por um sinal de mais, isso indica n
mais argumentos. Você pode assumir que todos os argumentos dados a uma função são do mesmo tipo, a menos que seja especificado de outra forma. Você também pode assumir que, se nenhum comportamento for especificado para um tipo de certificado, você poderá assumir que nenhum argumento dessa função será desse tipo. Os argumentos serão mencionados no diagrama a seguir:
(func argument1 argument2 ... argumentn)
+ , 2+
- Se todos os argumentos forem do tipo Inteiro , você deverá retornar a soma dos argumentos
- Se todos os argumentos forem do tipo Lista , você deverá retornar a concatenação dos argumentos em ordem crescente (
arg1+arg2+ ...
) - Se todos os argumentos forem do tipo Booleano , você deverá retornar a lógica All da sequência de argumentos
(+ 1 2 3 4 5) -> 15
(+ (list 1 2) (list 3 4)) -> (list 1 2 3 4)
(+ true true true) -> true
- , 2+
- Se todos os argumentos forem do tipo Inteiro , você deverá retornar a diferença dos argumentos (
arg1-arg2- ...
) - Se todos os argumentos forem do tipo Booleano , você deverá retornar o lógico Any da sequência de argumentos
(- 8 4 3) -> 1
(- 0 123) -> -123
(- true false false true false) -> true
- Se todos os argumentos forem do tipo Inteiro , você deverá retornar a diferença dos argumentos (
* , 2+
- Se todos os argumentos forem do tipo Inteiro , você deverá retornar o produto dos argumentos
- Se um argumento for do tipo List e o outro for do tipo Inteiro (você pode assumir que esses serão apenas os únicos argumentos fornecidos), você deve retornar uma nova lista com os itens
arg1
repetidamentearg2
. (* 1 2 3 4 5) -> 120
(* (list 1 2 3) 2) -> (list 1 2 3 1 2 3)
/ , 2+
- Se todos os argumentos forem do tipo Inteiro , você deverá retornar o quociente dos argumentos (
arg/arg2/ ...
) (você pode assumir que a divisão é feita seqüencialmente e que a parte decimal em cada etapa é truncada) - Se um argumento for do tipo List e o outro for do tipo Function , você deverá retornar a Lista resultante depois de
arg2
mapeada sobre cada valor (/ 100 10 3) -> 3
(/ (list 1 2 3) inc) -> (list 2 3 4)
- Se todos os argumentos forem do tipo Inteiro , você deverá retornar o quociente dos argumentos (
% 2
- Se todos os argumentos forem do tipo Inteiro , você deverá retornar o módulo dos argumentos
(% 4 2) -> 0
= , 2+
- Se tanto o tipo e valor de todos os argumentos é o mesmo, você deve retornar verdadeiro. Caso contrário, retorne false.
(= 0 0 0) -> true
(= 0 false (list)) -> false
lista , 0+
- Você deve retornar uma lista de todos os argumentos, independentemente do tipo. Se nenhum argumento for fornecido, você deverá retornar uma lista vazia
(list 3 4 (list 5)) -> (list 3 4 (list 5))
inc , 1
- Se o argumento for do tipo Inteiro , você deverá retornar o Inteiro incrementado em um
- Se o argumento for do tipo Lista , você deve retornar a Lista girada no sentido horário uma única rotação
(inc 1) -> 2
(inc (list 1 2 3)) -> (list 3 1 2)
dez , 1
- Se o argumento for do tipo Inteiro , você deve retornar o Inteiro decrementado por um
- Se o argumento for do tipo Lista , você deve retornar a Lista girada no sentido anti-horário uma única rotação
(dec 1) -> 0
(dec (list 1 2 3)) -> (list 2 3 1)
se 3
- Se houver três argumentos de qualquer tipo: Se o valor
arg1
verdadeiro de for verdadeiro, retornearg2
, caso contrário, retornearg3
(if (not (list 1)) 8 false) -> false
- Se houver três argumentos de qualquer tipo: Se o valor
não 1
- Se for fornecido um argumento de qualquer tipo, se o valor de verdade
arg1
for False, retornetrue
, caso contrário, retornefalse
. (not (list)) -> true
- Se for fornecido um argumento de qualquer tipo, se o valor de verdade
len , 1
- Se for fornecido um argumento do tipo List , retorne o comprimento de
arg1
(len (list 4 2 true (list 3) (list))) -> 5
- Se for fornecido um argumento do tipo List , retorne o comprimento de
Tabela da verdade:,
0, (list), false -> false
onde (list)
denota uma lista vazia. Tudo o resto é true
.
Seu intérprete pode ser um programa completo que lê a entrada de origem de stdin ou um arquivo ou uma função que pega a fonte como uma string e retorna o valor de saída.
Se escolher o primeiro, a saída para Inteiros é simplesmente números, para Booleanos é true
ou false
e para listas é uma sequência de valores separada por espaço entre colchetes (por exemplo, (1 2 3 4 (5 6 7))
denota (list 1 2 3 4 (list 5 6 7))
).
Se você escolher este último, o valor deverá ser retornado no tipo correspondente da linguagem de implementação ou, se não houver um tipo semelhante, em um tipo personalizado. As listas podem ser retornadas como matrizes ou vetores se o idioma não tiver um tipo de lista , os booleanos devem ser retornados como um tipo booleano no idioma ou um tipo personalizado se o idioma não os suportar.
Casos de teste
(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
Esclarecimentos
- Seu intérprete pode lidar com entradas inválidas da maneira que você escolher, mas não deve gerar uma exceção (porém, pode imprimir uma mensagem de erro e sair sem problemas)
- As funções sempre avaliarão os argumentos da esquerda para a direita
- Entrada inválida é qualquer entrada sintaticamente incorreta. Isso inclui, mas não se limita a, colchetes incompatíveis, divisão por zero e funções parcialmente aplicadas (a menos que o bônus seja concedido)
- Para
=
, se algum dos valores for diferente ou algum dos tipos for diferente, retornefalse
Bónus
- Pontuação * 0,8 se você suportar funções parcialmente aplicadas. Por exemplo,
((+ 2) 3)
seria o mesmo que(+ 2 3)
, mas permite coisas como(/ (list 1 2 3) (+ 2))
. Você pode assumir que uma função é parcialmente aplicada se receber menos que seu número mínimo de argumentos - Pontuação * 0,85 se você não avaliar os argumentos aplicados, a
if
menos que eles retornem
Isso é código-golfe, então o intérprete com a menor contagem de bytes vence!
(+ 3 (if false 5))
? De um modo geral, o que realmente é "nada devolvendo"? Você não especificou qualquer tipo de unidade a ser afinados
(+ bool bool...)
AND (- bool bool...)
lógico e OR lógico? A notação de toque padrão seria usada +
para OR e *
para AND. 2. A "entrada inválida" destina-se a cobrir casos como (/ 2 0)
os sintaticamente corretos? 3. Pois =
, se os valores não são todos iguais, deve retornar false
? 4. A definição de not
parece estar ao contrário. 5. Quais são os tokens? Você diz que o intérprete deve lidar com espaço em branco extra, mas não diz em que espaço em branco ele pode confiar. Para perguntas complexas como essa, você realmente deve usar a sandbox para que as especificações possam ser verificadas.
((+ 2 3) 4)
igual 9
ou é um erro? Notavelmente, para funções var-arg, não está claro quando se deve considerar o aplicativo parcial. Ele fica ainda mais enlameado com coisas como((if true (+ 2 3) (- 5)) 4)
(if (not (array 1)) 8 false) -> false
?