Isso é quase Lisp!

14

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 truee falsesã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 nmais 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
  • * , 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 arg1repetidamente arg2.
    • (* 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 arg2mapeada sobre cada valor
    • (/ 100 10 3) -> 3
    • (/ (list 1 2 3) inc) -> (list 2 3 4)
  • % 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 arg1verdadeiro de for verdadeiro, retorne arg2, caso contrário, retornearg3
    • (if (not (list 1)) 8 false) -> false
  • não 1

    • Se for fornecido um argumento de qualquer tipo, se o valor de verdade arg1for False, retorne true, caso contrário, retorne false.
    • (not (list)) -> true
  • len , 1

    • Se for fornecido um argumento do tipo List , retorne o comprimento dearg1
    • (len (list 4 2 true (list 3) (list))) -> 5

Tabela da verdade:, 0, (list), false -> falseonde (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 é trueou falsee 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 ifmenos que eles retornem

Isso é código-golfe, então o intérprete com a menor contagem de bytes vence!

globby
fonte
Como se interpreta (if (not (array 1)) 8 false) -> false?
precisa saber é
@feersum captura boa, é suposto ser 8.
globby
1
Como devemos avaliar (+ 3 (if false 5))? De um modo geral, o que realmente é "nada devolvendo"? Você não especificou qualquer tipo de unidade a ser afinados
haskeller orgulhoso
3
1. Por que (+ 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 notparece 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.
Peter Taylor
1
não está claro como a aplicação parcial deve funcionar: é ((+ 2 3) 4)igual 9ou é 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)
MtnViewMark

Respostas:

6

Haskell, 1370 1263 1179 1128 1163 1107 1084 bytes * 0,8 * 0,85 = 737,12

import Text.Parsec
data V=I Int|L[V]|T|F|P[V]|X|A|S|M|D|U|E|Q|J|K|C|N|W deriving Eq
m Q=0;m C=3;m f|f`elem`[J,K,N,W]=1;m _=2
l=length
x v=[n|I n<-v]
y v=[l|L l<-v]
z v=[0<1|T<-v]++[1<0|F<-v]
(&)f=l.f>>=(.l).(==)
b a|a=T|0<1=F
s(I n)=show n
s(L v)='(':tail(v>>=(' ':).s)++")"
s T=d!!0;s F=d!!1;s _="error"
i(L v)=e$i%v
i v=v
e(P v:a)=e$v++a
e(f:a)|m f>l a=P(f:a)
e(A:a)|x&a=I$sum$x a|y&a=L$concat$y a|z&a=b$and$z a
e(S:a)|x&a=I$f$x a|z&a=b$or$z a
e(M:a)|x&a=I$product$x a
e[M,v,I n]=e$A:replicate n v
e(D:a)|x&a=I$v$x a
e[D,L v,f]=L$map(\a->e[f,a])v
e[U,I a,I b]=I$a`mod`b
e(E:a:v)=b$all(==a)v
e(Q:a)=L a
e[J,I a]=I$a+1
e[J,L[]]=L[]
e[J,L v]=L$last v:init v
e[K,I a]=I$a-1
e[K,L v]=L$drop 1 v++take 1 v
e[C,a,b,c]|a`elem`[I 0,L[],F]=c|0<1=b
e[N,a]=e[C,a,F,T]
e[W,L v]=I$l v
e _=X
f(a:b)=a-sum b
v(a:b)=foldl div a b
(%)f=fmap f
p=k$choice$try%([(I .read)%many1 digit,L%between(w"(")(k$w")")(many$try p)]++zipWith((.return).(>>).w)d[T,F,A,S,M,D,U,E,Q,J,K,C,N,W])
k=(spaces>>)
w=string
d=words"true false + - * / % = list inc dec if not len"
g=either show(s.i).parse p""
main=interact g

Programa completo, lendo stdine escrevendo para stdout. 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):

λ: g "(list 1 2 3 (list 4 5 true))"
(1 2 3 (4 5 true))

λ: g "(/ 4000 (+ 1 2 3 4 (* 5 8)))"
80

λ: g "(+ (not (- (len (list 5 6 7)) (/ 10 3))) true)"
true

λ: g "(if (           len (list )  ) 4 (if    (+ (= 8 8    8) (not (list 4))) 8 5))"
5

λ: g "(if false (/ 1 0) 5)"
5

λ: g "((+ 2) 3)"
5

λ: g "(/ (list 1 2 3) (+ 2))"
(3 4 5)

Agora tem todos os testes de unidade da descrição:

λ: runTests 
passed: g "(+ 1 2 3 4 5)" ==> 15
passed: g "(+ (list 1 2) (list 3 4))" ==> (1 2 3 4)
passed: g "(+ true true true)" ==> true
passed: g "(- 8 4 3)" ==> 1
passed: g "(- 0 123)" ==> -123
passed: g "(- true false false true false)" ==> true
passed: g "(* 1 2 3 4 5)" ==> 120
passed: g "(* (list 1 2 3) 2)" ==> (1 2 3 1 2 3)
passed: g "(/ 100 10 3)" ==> 3
passed: g "(/ (list 1 2 3) inc)" ==> (2 3 4)
passed: g "(% 4 2)" ==> 0
passed: g "(= 0 0 0)" ==> true
passed: g "(= 0 false (list))" ==> false
passed: g "(list 3 4 (list 5))" ==> (3 4 (5))
passed: g "(inc 1)" ==> 2
passed: g "(inc (list 1 2 3))" ==> (3 1 2)
passed: g "(dec 1)" ==> 0
passed: g "(dec (list 1 2 3))" ==> (2 3 1)
passed: g "(if (not (list 1)) 8 9)" ==> 9
passed: g "(not (list))" ==> true
passed: g "(len (list 4 2 true (list 3) (list)))" ==> 5
passed: g "(list 1 2 3 (list 4 5 true))" ==> (1 2 3 (4 5 true))
passed: g "(/ 4000 (+ 1 2 3 4 (* 5 8)))" ==> 80
passed: g "(+ (not (- (len (list 5 6 7)) (/ 10 3))) true)" ==> true
passed: g "(if (           len (list )  ) 4 (if    (+ (= 8 8    8) (not (list 4))) 8 5))" ==> 5
passed: g "(if false (/ 1 0) 5)" ==> 5
passed: g "((+ 2) 3)" ==> 5
passed: g "(/ (list 1 2 3) (+ 2))" ==> (3 4 5)
MtnViewMark
fonte
b os casos e[K,L _]que você poderia usar drop 1 como uma versão segura de taile o uso takede uma versão segura de headunir as duas definições dee[K,L _]
haskeller orgulhoso
você pode usar a função. notElemOutra dica: você pode fazer s=stringe usá-lo em vez de ambos stringe char( s"C"vs. char 'C'). Outra dica: use guardas em vez de ifs
haskeller orgulhoso
Outra coisa em que pensei: você pode codificar Maybevalores por listas. Nothingé []e Just 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.
haskeller orgulhoso
@proudhaskeller Obrigado, todos aplicados!
MtnViewMark
4

Python 2, 1417 * 0,8 * 0,85 = 963,56

from operator import*
A=type;K="list"
def E():print"E";exit()
def R(G):
 len(G)or E();T=G.pop(0);L=[]
 if"("==T:
  G or E()
  while")"!=G[0]:L+=[R(G)];G or E()
  G.pop(0);return L
 if")"==T:E()
 try:
  x=eval(T.title())
  if Q(x)<2:return x
  E()
 except:return T
H="+ - * / = % if inc dec not len"
Z=lambda y:lambda x:reduce(y,x)
D=dict(zip(H.split(),[[sum,any,0,lambda x:sum((y[1:]for y in x),[K])],[Z(sub)],[Z(mul),all,0,lambda x:x[0][:1]+x[0][1:]*x[1]],[Z(div),lambda x:[K]+map(lambda z:S([x[1],z]if Q(x[1])==2else x[1]+[z]),x[0][1:])],[lambda x:len(set(map(str,x)))<2]*6,[lambda x:x[0]%x[1]],[lambda x:S(x[2])if S(x[0])in[0,[K]]else S(x[1])]*6,[lambda x:x[0]+1,0,0,lambda x:x[0][:1]+x[0][-1:]+x[0][1:-1]],[lambda x:x[0]-1,0,0,lambda x:x[0][:1]+x[0][2:]+[x[0][1]]],[lambda x:x[0]in[0,[K]]]*6,[0]*3+[lambda x:len(x)-1]]))
H=H[:15]+H+" if"
def Q(x):
 t=A(x);w=int,bool,str
 if t in w:return w.index(t)
 if t==list and x:return 5-(2*(x[0]==K)+(str==A(x[0])and len(x)<H.count(x[0])+1))
 E()
def S(G):
 if Q(G)<2:return G
 G or E();c=G[0];r=G[1:];c==K or r or E()
 if c!="if":r=[x if Q(x)in{2,4}else S(x)for x in r]
 if c==K:return[c]+r
 k=map(Q,r);m=min(k);M=max(k);v=[m,[-1,3][{m,M}=={4,5}]][m!=M]
 try:return D[c][v](r)
 except:E()
def C(x):return"(%s)"%" ".join(map(C,x))if A(x)==list else str(x).lower()
def I(G):
 for c in"+-*/%=()":G=G.replace(c," %s "%c)
 return C(S(R(G.strip().split())))
print I(raw_input())

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 :

import base64,zlib
exec zlib.decompress(base64.b64decode("eJx9VE1P4zAQvedXGEuV7MbttgX2kOADAtSugANbTljWKqSuNku+5Lg0BfHfd8ZJCwjt9tLpdN6bmTczXtuqIFVtbOIqS7KirqwbBufS7WoTX0uaZ42jwcqsyRXjUW2z0tErGps2c4x7/08251FAclOCARwQF9/L+biuajbh8Y1UOiDZmjIq5T0EkjnposDc/s5yQzk9knM10dFNKBXS6fhDzIHJGrexJbnxbNyz+Qhnd0jbSvOc5Ox+7DKXG8YRm63JHWv52SzqwS04Pci0qand3n0fLCQNyYgMyTciyQCBWZmSlUlJWTlsjgYPMk+Kx1VCdlFvtIBfbVLDdqLlwaVcZaljL1nNFuOmzlEhoVSzKURS7sREHFDgYmynppFeQ5s7SEVaCL3WXAv1wJrNY2cUm5yLJM8/YlsQSkVTHXoDKIatmmofvsqe+Xsg0IVFUrPe8RItmcJQ8aI7WcDmUs5M3hiCP0L1ornY02IFBy4cbmMcQ77GWeiWg6h6+P1DDAIHfS0H5xLSzDSHhGhNwCrVBDvVPu2yq+IrUTiFnv/Z9Qjq2/c/+pwQvaP/gmeAVR1Yf4EeyvMlTfTwOPysQssxISzXQi6A81SHi5DiQvpbwGWDXXTyHIx4K+FaxGNV5QJEw7UlDme93a/ddpyVK9Myx7s/pcRzI0m58qvlY05HbDb02kl5zUOUXyI9iomBXVFni3FabUrX+cMpbv9Vf6DL7kD90OcfbmEeHE4xTv0Bxha+QFv4Ka/xL3s4Q0CnR5JCo5GVqt1fVla+zsTJ236YHPe5xR6t7jBA1OdTqQ5BhCeJS3QnLI8LWWQle+LxLfhaNJ6lKgSMVxxr9VqI2zcpX0/E6ZvWqjiSt7o79r7+S2BUz5rZ93Pet3yBc+jCKBs0nA4ooeM/FaTD7Be4wFAdTqnX3HcA2oJnnFdbY3umH5142FcKfdFwNPw2kIzTaA5vnDV1nsD9p4KSQUPoIIVa+vIu2JLBYzYGUngR+P5FgE/gn1Ggtsn2V1bWG3T/BUW+qRU="))

Nota: Se a minha pontuação subir, provavelmente é porque encontrei alguns bugs

Sp3000
fonte
mais um desafio de código do que um código de golfe, mas ainda assim, 4872 * 0,8 = 3897,6
Def
3

Lisp comum, 868 bytes * 0,85 = 737,8

É trapaça implementar o Lisp com o Lisp? Muito para otimizar aqui, ainda.

(SETF (READTABLE-CASE *READTABLE*) :PRESERVE)(PRINC(LABELS((B(X)(FIND X'(true false)))(R(X)(IF X'true'false))(V(X)(MEMBER X'(0()false)))(A(&REST X)(R(NOTANY #'V X)))(O(&REST X)(R(NOTEVERY #'V X)))(E(X &KEY N)(IF(LISTP X)(ECASE(FIRST X)(+(APPLY(IF(EVERY'NUMBERP #1=(MAPCAR(IF N #'IDENTITY #'E)(CDR X)))'+(IF(EVERY'LISTP #1#)'APPEND #'A))#1#))(-(APPLY(IF(EVERY'NUMBERP #1#)'- #'O)#1#))(*(IF(LISTP #2=(CAR #1#))(LOOP FOR I TO(1-(CADR #1#))APPEND #2#)(APPLY'* #1#)))(/(IF(LISTP #2#)(LOOP FOR I IN #2#COLLECT(E `(,(CADR #1#),I):N T))(REDUCE'FLOOR #1#)))(%(APPLY'MOD #1#))(=(R(LOOP FOR I IN(CDR #1#)ALWAYS(EQUAL I #2#))))(list #1#)(inc(IF(LISTP #2#)(APPEND(LAST #2#)(BUTLAST #2#))(1+ #2#)))(dec(IF(LISTP #2#)(APPEND(CDR #2#)`(,(FIRST #2#)))(1- #2#)))(if(IF(V(E(CADR X)))(E(CADDDR X))(E(CADDR X))))(not(R(V #2#)))(len(LENGTH #2#)))X)))(OR(IGNORE-ERRORS(OR(E(READ))"()")):E))

Imprime um E em caso de erro na entrada. Amostras de execuções:

$ sbcl --script glisp.lisp
(list 1 2 3 (list 4 5 true))
(1 2 3 (4 5 true))

$ sbcl --script glisp.lisp
(/ 4000 (+ 1 2 3 4 (* 5 8)))
80

$ sbcl --script glisp.lisp
(+ (not (- (len (list 5 6 7)) (/ 10 3))) true)
true

$ sbcl --script glisp.lisp
(if (           len (list )  ) 4 (if    (+ (= 8 8    8) (not (list 4))) 8 5))
5

$ sbcl --script glisp.lisp
(this is an error)
E

$ sbcl --script glisp.lisp
(if (% 4 2) (this is an error) 42)
42
jlahd
fonte
2
contanto que não é um tipo de função eval ...
Def
2

Haskell, 972

r=fst.f
f('(':s)|(f:a,b)<-g s=(f%filter(/="")a,b)
f s=span(`notElem`" ()")s
j=dropWhile(==' ')
g""=([],"")
g s|')':l<-r=([x],l)|(y,t)<-g$j r=(x:y,t)where(x,r)=f$j s
"%"%c=show$foldr(mod.read)(maxBound::Int)c
"+"%c|t(c!!0)<1="(list "++tail(c>>=(' ':).drop 6.init)++")"|t(c!!0)<2=show$sum$map read c|0<1=i$all((=='t').head)c
"-"%c|t(c!!0)<2=show$foldl1(-)$map read c|0<1=i$any((=='t').head)c
"*"%c=fst$f$"(+ "++unwords([1..read$last c]>>init c)++")"
"="%c=i$all(==c!!0)c
"/"%c|t(c!!0)<1,[a,b]<-c="list"%map(\x->b%[x])(fst$g$drop 6 a)|0<1=show$foldl1 div$map read c
"if"%[p,a,b]|elem p["0","()","false"]=b|0<1=a
"list"%c="(list "++unwords c++")"
"len"%[c]=show$length(words c)-1
"inc"%[c]|t c>0=show$read c+1|([],_)<-g$drop 6 c="(list)"|(x,_)<-g$drop 6 c="list"%(last x:init x)
"dec"%[c]|t c<1,(x,_)<-g$drop 6 c="list"%(drop 1 x++take 1 x)|0<1=show$read c-1
"not"%[c]="if"%[c,"false","true"]
s%c="?"
i p|p="true"|0<1="false"
t('(':_)=0
t(c:s)|c<':',c>'/'=1|elem c"th"=2
t _=3

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..9para números, (listas tou fbooleanos e tudo mais para funções.

para executar use a rfunção

orgulhoso haskeller
fonte