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!
fonte
(if (not (array 1)) 8 false) -> false
?(+ 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 retornarfalse
? 4. A definição denot
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)
igual9
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)
Respostas:
Haskell,
1370126311791128116311071084 bytes * 0,8 * 0,85 = 737,12Programa completo, lendo
stdin
e escrevendo parastdout
.g
é a versão da função também.Implementa funções parciais e avaliação lenta de
if
.Amostras de execuções (da versão da função):
Agora tem todos os testes de unidade da descrição:
fonte
e[K,L _]
que você poderia usardrop 1
como uma versão segura detail
e o usotake
de uma versão segura dehead
unir as duas definições dee[K,L _]
notElem
Outra dica: você pode fazers=string
e usá-lo em vez de ambosstring
echar
(s"C"
vs.char 'C'
). Outra dica: use guardas em vez deif
sMaybe
valores por listas.Nothing
é[]
eJust x
é[x]
. Isso elimina os construtores longos e adiciona mais algumas funcionalidades:if p then Just x else Nothing
é[x|p]
,(==Nothing)
énull
, a mônada da lista se torna a mesma que a mônada talvez, e assim por diante.Python 2, 1417 * 0,8 * 0,85 = 963,56
Revisão completa. Se você quiser ver as versões anteriores, consulte o histórico de edições .
Há muito mais para jogar golfe. Estou lentamente trabalhando nisso.
Com zlib / base64, obtemos 1093 * 0,8 * 0,85 = 743,24 :
Nota: Se a minha pontuação subir, provavelmente é porque encontrei alguns bugs
fonte
Lisp comum, 868 bytes * 0,85 = 737,8
É trapaça implementar o Lisp com o Lisp? Muito para otimizar aqui, ainda.
Imprime um E em caso de erro na entrada. Amostras de execuções:
fonte
Haskell, 972
uma solução bastante hacky. isso armazena tudo como seqüências de caracteres na forma pronta para saída - seus tipos podem ser distinguidos pela primeira letra -
0..9
para números,(
listast
ouf
booleanos e tudo mais para funções.para executar use a
r
funçãofonte