È quasi Lisp!


14

Sfida

La tua sfida è progettare un interprete per un linguaggio simile a lisp, che d'ora in poi sarà coniato: GLisp . Il codice del programma per GLisp sarà costituito da una quantità arbitraria di espressioni nidificate indicate da parentesi, nella seguente forma:

(func arg1 arg2 ...)

Si noti che l'interprete deve consentire caratteri di spazi bianchi estranei prima e dopo parentesi, funzioni e argomenti.

tipi

Implementerai quattro tipi, Intero, Elenco, Booleano e Funzione. I valori interi e booleani possono essere esplicitamente inseriti nel codice sorgente con la propria sintassi. L'interprete deve presumere che una sequenza di caratteri numerici denoti un numero intero (non è necessario implementare una sintassi per inserire esplicitamente numeri interi negativi). L'interprete deve anche presumere che truee falsesiano designati valori booleani. Le funzioni non possono essere definite esplicitamente dall'utente e restituiranno sempre un singolo valore (un Elenco di qualsiasi lunghezza conta come un singolo valore).

funzioni

Le seguenti funzioni devono essere implementate e sono nel formato Funzione , Arity . Se un Arity nè seguito da un segno più, allora questo indica no più argomenti. Si può presumere che tutti gli argomenti forniti a una funzione siano dello stesso tipo, se non diversamente specificato. Si può anche presumere che se non viene specificato alcun comportamento per un tipo di certificato, si può presumere che nessun argomento di quella funzione sarà mai di quel tipo. Gli argomenti verranno indicati come nel seguente diagramma:

(func argument1 argument2 ... argumentn)

  • + , 2+

    • Se tutti gli argomenti sono del tipo Intero , è necessario restituire la somma degli argomenti
    • Se tutti gli argomenti sono del tipo Elenco , è necessario restituire la concatenazione degli argomenti in ordine crescente ( arg1+arg2+ ...)
    • Se tutti gli argomenti sono di tipo booleano , è necessario restituire tutti gli argomenti logici della sequenza
    • (+ 1 2 3 4 5) -> 15
    • (+ (list 1 2) (list 3 4)) -> (list 1 2 3 4)
    • (+ true true true) -> true
  • - , 2+

    • Se tutti gli argomenti sono del tipo Intero , è necessario restituire la differenza degli argomenti ( arg1-arg2- ...)
    • Se tutti gli argomenti sono di tipo booleano , è necessario restituire il logico Qualsiasi della sequenza di argomenti
    • (- 8 4 3) -> 1
    • (- 0 123) -> -123
    • (- true false false true false) -> true
  • * , 2+

    • Se tutti gli argomenti sono di tipo Intero , è necessario restituire il prodotto degli argomenti
    • Se un argomento è di tipo Elenco e l'altro è di tipo Intero (si può presumere che questi saranno gli unici argomenti forniti), è necessario restituire un nuovo Elenco con gli elementi in tempi arg1ripetuti arg2.
    • (* 1 2 3 4 5) -> 120
    • (* (list 1 2 3) 2) -> (list 1 2 3 1 2 3)
  • / , 2+

    • Se tutti gli argomenti sono del tipo Integer , è necessario restituire il quoziente degli argomenti ( arg/arg2/ ...) (è possibile supporre che la divisione venga eseguita in modo sequenziale e che la parte decimale in ogni passaggio venga troncata)
    • Se un argomento è di tipo Elenco e l'altro è di tipo Funzione , è necessario restituire l' elenco risultante dopo che arg2è stato mappato su ogni valore
    • (/ 100 10 3) -> 3
    • (/ (list 1 2 3) inc) -> (list 2 3 4)
  • % , 2

    • Se tutti gli argomenti sono del tipo Intero , è necessario restituire il modulo degli argomenti
    • (% 4 2) -> 0
  • = , 2+

    • Se sia il tipo e il valore di tutti gli argomenti è la stessa, è necessario tornare vero. Altrimenti, restituisci false.
    • (= 0 0 0) -> true
    • (= 0 false (list)) -> false
  • elenco , 0+

    • È necessario restituire un elenco di tutti gli argomenti, indipendentemente dal tipo. Se non viene fornito alcun argomento, è necessario restituire un elenco vuoto
    • (list 3 4 (list 5)) -> (list 3 4 (list 5))
  • inc , 1

    • Se l'argomento è di tipo Integer , è necessario restituire Integer incrementato di uno
    • Se l'argomento è di tipo Elenco , è necessario restituire l' elenco ruotato in senso orario di una singola rotazione
    • (inc 1) -> 2
    • (inc (list 1 2 3)) -> (list 3 1 2)
  • dic , 1

    • Se l'argomento è di tipo Numero intero , è necessario restituire il valore intero decrementato di uno
    • Se l'argomento è di tipo Elenco , è necessario restituire l' elenco ruotato in senso antiorario di una singola rotazione
    • (dec 1) -> 0
    • (dec (list 1 2 3)) -> (list 2 3 1)
  • se , 3

    • Se vengono forniti tre argomenti di qualsiasi tipo: Se il valore di verità arg1è vero, restituisce arg2, altrimenti restituiscearg3
    • (if (not (list 1)) 8 false) -> false
  • no , 1

    • Se viene fornito un argomento di qualsiasi tipo, se il valore di verità arg1è False, return true, else return false.
    • (not (list)) -> true
  • len , 1

    • Se viene fornito un argomento di tipo Elenco , restituisce la lunghezza diarg1
    • (len (list 4 2 true (list 3) (list))) -> 5

Tabella di verità:, 0, (list), false -> falsedove (list)indica un elenco vuoto. Tutto il resto è true.

L'interprete può essere un programma completo che legge l'input di origine da stdin o un file o una funzione che accetta l'origine come stringa e restituisce il valore di output.

Se si sceglie il primo, l'output per Integer è semplicemente un numero, per Booleans è trueo false, e per lists è una sequenza di valori separata da spazi racchiusa tra parentesi (ad es. (1 2 3 4 (5 6 7))Indica (list 1 2 3 4 (list 5 6 7))).

Se si sceglie quest'ultimo, il valore deve essere restituito nel tipo corrispondente del linguaggio di implementazione o, se non esiste un tipo simile, un tipo personalizzato. Le liste possono essere restituiti come array o vettori se la lingua non ha un elenco tipo, booleani dovrebbe essere restituito come un tipo booleano nella lingua, o un tipo personalizzato, se la lingua non li supporta.

Casi test

(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

chiarimenti

  • L'interprete può gestire input non validi nel modo che preferisci, ma non deve generare un'eccezione (tuttavia, può stampare un messaggio di errore ed uscire senza problemi)
  • Le funzioni valuteranno sempre gli argomenti da sinistra a destra
  • L'input non valido è qualsiasi input sintatticamente errato. Questo include, ma non è limitato a, parentesi non corrispondenti, divisione per zero e funzioni parzialmente applicate (a meno che non vada per il bonus)
  • Per =, se uno dei valori sono differenti o qualsiasi dei tipi sono diversi, ritornofalse

bonus

  • Punteggio * 0,8 se si supportano funzioni parzialmente applicate. Ad esempio, ((+ 2) 3)sarebbe lo stesso di (+ 2 3), ma consente cose come (/ (list 1 2 3) (+ 2)). Si può presumere che una funzione sia parzialmente applicata se riceve meno del suo numero minimo di argomenti
  • Punteggio * 0,85 se non si valutano gli argomenti applicati a ifmeno che non vengano restituiti

Questo è code-golf, quindi vince l'interprete con il numero di byte più basso!


Come si interpreta (if (not (array 1)) 8 false) -> false?
feersum

@feersum buona cattura, dovrebbe essere 8.
globby

1
Come dovremmo valutare (+ 3 (if false 5))? In generale, cosa significa veramente "non restituire nulla"? Non hai specificato alcun tipo di unità da
risintonizzare

3
1. Perché è (+ bool bool...)AND (- bool bool...)logico e OR logico? La notazione suoneria standard userebbe +per OR e *per AND. 2. Gli "input non validi" intendono coprire casi come (/ 2 0)quelli sintatticamente corretti? 3. Perché =, se i valori non sono tutti uguali, dovrebbe restituire false? 4. La definizione di notsembra essere al contrario. 5. Quali sono i token? Dici che l'interprete deve gestire spazi bianchi extra, ma non dici su quale spazio bianco può fare affidamento. Per domande complesse come questa, dovresti davvero utilizzare il sandbox in modo che le specifiche possano essere verificate.
Peter Taylor,

1
non è chiaro come dovrebbe funzionare l'applicazione parziale: è ((+ 2 3) 4)uguale 9o un errore? In particolare, per le funzioni var-arg, non è chiaro quando si dovrebbe considerare l'applicazione parziale. Diventa ancora più confuso con cose come((if true (+ 2 3) (- 5)) 4)
MtnViewMark

Risposte:


6

Haskell, 1370 1263 1179 1128 1163 1107 1084 byte * 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

Programma completo, lettura stdine scrittura a stdout. gè anche la versione della funzione.

Implementa entrambe le funzioni parziali e la valutazione pigra di if.

Esecuzioni di esempio (della versione della funzione):

λ: 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)

Ora ha tutti i test unitari dalla descrizione:

λ: 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)

b i casi e[K,L _]che potresti usare drop 1 come versione sicura di taile utilizzare takeper una versione sicura di headunione delle due definizioni die[K,L _]
orgoglioso haskeller

puoi usare la funzione notElem.un altro suggerimento: puoi farlo s=stringe usarlo al posto di entrambi stringe char( s"C"vs. char 'C'). Un altro suggerimento: usare le guardie al posto di ifs
haskeller orgogliosi

Un'altra cosa a cui ho pensato: puoi codificare i Maybevalori in base agli elenchi. Nothingè []ed Just xè [x]. Questo elimina i lunghi costruttori e aggiunge altre funzionalità: if p then Just x else Nothingè [x|p], (==Nothing)è null, la monade elenco diventa la stessa della monade forse, e così via.
orgoglioso haskeller il

@proudhaskeller Grazie, tutti applicati!
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())

Revisione completa. Se vuoi vedere le versioni precedenti, controlla la cronologia delle modifiche .

C'è molto di più da giocare a golf. Ci sto lentamente lavorando.

Con zlib / base64 otteniamo 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 vedi che il mio punteggio aumenta, probabilmente è perché ho trovato alcuni bug


più di una sfida al codice di un codice golf ma comunque, 4872 * 0,8 = 3897,6
Def

3

Lisp comune, 868 byte * 0,85 = 737,8

Imbrogli per implementare Lisp con Lisp? Molto da ottimizzare qui, ancora.

(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))

Stampa una E in caso di errore nell'input. Esecuzioni campione:

$ 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

2
purché non sia una specie di funzione di valutazione ...
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

una soluzione piuttosto confusa. questo memorizza tutto come stringhe in forma pronta per l'output (i loro tipi possono essere distinti dalla loro prima lettera) 0..9per numeri, (elenchi to fbooleani e tutto il resto per funzioni.

per eseguire utilizzare la rfunzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.