Это почти Лисп!

14

Вызов

Ваша задача состоит в том, чтобы разработать переводчик для языка, похожего на шутки , который в дальнейшем будет придуман: GLisp . Программный код для GLisp будет состоять из произвольного количества вложенных выражений, обозначенных скобками, в следующем виде:

(func arg1 arg2 ...)

Обратите внимание, что интерпретатор должен допускать использование лишних пробельных символов до и после скобок, функций и аргументов.

Типы

Вы будете реализовывать четыре типа: Integer, List, Boolean и Function. Целые числа и логические значения могут быть явно вставлены в исходный код с их собственным синтаксисом. Ваш интерпретатор должен предположить, что серия числовых символов обозначает целое число (вам не нужно реализовывать синтаксис для явной вставки отрицательных целых чисел). Ваш интерпретатор также должен предположить, что trueи falseобозначены как логические значения. Функции не могут быть явно определены пользователем и всегда будут возвращать одно значение (список любой длины считается одним значением).

функции

Следующие функции должны быть реализованы в формате Function , Arity . Если Arity nсопровождается знаком плюс, то это означает nили более аргументов. Вы можете предположить, что все аргументы, переданные функции, относятся к одному типу, если не указано иное. Вы также можете предположить, что если не определено поведение для определенного типа, то вы можете предположить, что ни один аргумент этой функции никогда не будет такого типа. Аргументы будут называться на следующей диаграмме:

(func argument1 argument2 ... argumentn)

  • + , 2+

    • Если все аргументы имеют тип Integer , вы должны вернуть сумму аргументов
    • Если все аргументы имеют тип List , вы должны вернуть объединение аргументов в порядке возрастания ( arg1+arg2+ ...)
    • Если все аргументы имеют тип Boolean , вы должны вернуть логическую All из последовательности аргументов
    • (+ 1 2 3 4 5) -> 15
    • (+ (list 1 2) (list 3 4)) -> (list 1 2 3 4)
    • (+ true true true) -> true
  • - , 2+

    • Если все аргументы имеют тип Integer , вы должны вернуть разницу аргументов ( arg1-arg2- ...)
    • Если все аргументы имеют тип Boolean , вы должны вернуть логическую Any из последовательности аргументов
    • (- 8 4 3) -> 1
    • (- 0 123) -> -123
    • (- true false false true false) -> true
  • * , 2+

    • Если все аргументы имеют тип Integer , вы должны вернуть произведение аргументов
    • Если один аргумент имеет тип List, а другой - тип Integer (вы можете предполагать, что это будут только заданные аргументы), вы должны возвращать новый List с элементами в arg1повторяющееся arg2время.
    • (* 1 2 3 4 5) -> 120
    • (* (list 1 2 3) 2) -> (list 1 2 3 1 2 3)
  • / , 2+

    • Если все аргументы имеют тип Integer , вы должны вернуть частное значение arguments ( arg/arg2/ ...) (вы можете предположить, что деление выполняется последовательно, а десятичная часть на каждом шаге усекается)
    • Если один аргумент относится к типу List, а другой - к типу Function , вы должны вернуть итоговый список после того, arg2как он сопоставлен с каждым значением
    • (/ 100 10 3) -> 3
    • (/ (list 1 2 3) inc) -> (list 2 3 4)
  • % , 2

    • Если все аргументы имеют тип Integer , вы должны вернуть модуль аргументов
    • (% 4 2) -> 0
  • = , 2+

    • Если как тип и значение всех аргументов одно и то же, вы должны вернуть истинный. В противном случае верните false.
    • (= 0 0 0) -> true
    • (= 0 false (list)) -> false
  • список , 0+

    • Вы должны вернуть список всех аргументов, независимо от типа. Если аргументы не указаны, вы должны вернуть пустой список
    • (list 3 4 (list 5)) -> (list 3 4 (list 5))
  • вкл. , 1

    • Если аргумент имеет тип Integer , вы должны вернуть Integer, увеличенное на единицу
    • Если аргумент имеет тип List , вы должны вернуть список, повернутый по часовой стрелке за один оборот
    • (inc 1) -> 2
    • (inc (list 1 2 3)) -> (list 3 1 2)
  • декабрь 1

    • Если аргумент имеет тип Integer , вы должны вернуть Integer, уменьшенное на единицу
    • Если аргумент имеет тип List , вы должны вернуть список, повернутый против часовой стрелки за один оборот
    • (dec 1) -> 0
    • (dec (list 1 2 3)) -> (list 2 3 1)
  • если 3

    • Если даны три аргумента любого типа: если значение arg1true равно true, вернуть arg2, иначе вернутьarg3
    • (if (not (list 1)) 8 false) -> false
  • нет , 1

    • Если задан аргумент любого типа, если значение arg1true равно False, вернуть true, иначе вернуть false.
    • (not (list)) -> true
  • лен , 1

    • Если задан аргумент типа List , вернуть длинуarg1
    • (len (list 4 2 true (list 3) (list))) -> 5

Таблица правды:, 0, (list), false -> falseгде (list)обозначает пустой список. Все остальное есть true.

Ваш интерпретатор может быть либо полной программой, которая читает исходные данные из stdin или файла, либо функцией, которая принимает источник в виде строки и возвращает выходное значение.

Если выбрать первое, для Integer выводится просто число, для Booleans - trueили false, а для списков - последовательность значений, разделенных пробелами, заключенная в скобки (например, (1 2 3 4 (5 6 7))обозначает (list 1 2 3 4 (list 5 6 7))).

При выборе последнего значение должно быть возвращено в соответствующем типе языка реализации или, если нет аналогичного типа, в пользовательском типе. Списки могут быть возвращены как массивы или векторы , если язык не имеет список типа, Booleans должен быть возвращен в качестве логического типа в языке, или типа , если язык не поддерживает их.

Контрольные примеры

(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

Разъяснения

  • Ваш интерпретатор может иметь дело с неверным вводом любым способом, который вы выберете, но он не должен выдавать исключение (хотя он может распечатать сообщение об ошибке и завершить работу плавно)
  • Функции всегда будут оценивать аргументы слева направо
  • Неверный ввод - это любой ввод, который синтаксически неверен. Это включает в себя, но не ограничивается, несоответствующие скобки, деление на ноль и частично примененные функции (если не идет на бонус)
  • Для =, если любое из значений различны или любой из типов различны, возвратfalse

Бонусы

  • Оценка * 0,8, если вы поддерживаете частично примененные функции. Например, ((+ 2) 3)будет так же, как (+ 2 3), но учитывает такие вещи, как (/ (list 1 2 3) (+ 2)). Вы можете предположить, что функция применяется частично, если она получает меньше своего минимального количества аргументов.
  • Оценка * 0,85, если вы не оцениваете аргументы, применяемые к, ifесли они не будут возвращены

Это код-гольф, поэтому выигрывает интерпретатор с наименьшим количеством байтов!

globby
источник
Как можно интерпретировать (if (not (array 1)) 8 false) -> false?
feersum
@feersum хороший улов, должно быть 8.
сумасшедший
1
Как мы должны оценить (+ 3 (if false 5))? Вообще говоря, что на самом деле означает «ничего не возвращать»? Вы не указали тип юнита, который нужно перенастроить
гордый haskeller
3
1. Почему (+ bool bool...)логическое И и (- bool bool...)логическое ИЛИ? Стандартное обозначение кольца будет использоваться +для ИЛИ и *для И. 2. Предназначен ли «неверный ввод» для охвата случаев, подобных (/ 2 0)синтаксически правильным? 3. Ибо =, если значения не все одинаковы, должен ли он возвращаться false? 4. Определение notпредставляется задом наперед. 5. Какие токены? Вы говорите, что переводчик должен обрабатывать лишние пробелы, но вы не говорите, на какие пробелы он может положиться. Для сложных вопросов, подобных этому, вы действительно должны использовать песочницу, чтобы можно было проверить спецификацию.
Питер Тейлор
1
Непонятно, как должно работать частичное приложение: ((+ 2 3) 4)равно 9или ошибка? Примечательно, что для функций var-arg не ясно, когда следует рассматривать приложение как частичное. Это становится еще более грязным с такими вещами, как((if true (+ 2 3) (- 5)) 4)
MtnViewMark

Ответы:

6

Haskell, 1370 1263 1179 1128 1163 1107 1084 байта * 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

Полная программа, чтение stdinи запись в stdout. gэто версия функции, а также.

Реализует как частичные функции, так и ленивую оценку if.

Примеры прогонов (версии функции):

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

Теперь есть все юнит-тесты из описания:

λ: 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
источник
б случаи, которые e[K,L _]вы могли бы использовать drop 1 в качестве безопасной версии tailи использовать takeв качестве безопасной версии headсоединения двух определенийe[K,L _]
гордый haskeller
Вы можете использовать функцию. notElemДругой совет: вы можете сделать s=stringи использовать ее вместо обоих stringи char( s"C"против char 'C'). другой совет: используйте охранников вместо ifs
гордый haskeller
Еще одна вещь, о которой я подумал: вы можете кодировать Maybeзначения списками. Nothingесть []и Just xесть [x]. Это позволяет избавиться от длинных конструкторов и добавляет несколько функций: if p then Just x else Nothingэто [x|p], (==Nothing)есть null, список монада становится таким же , как , возможно , монады, и так далее.
гордый haskeller
@proudhaskeller Спасибо, все применено!
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())

Капитальный ремонт. Если вы хотите увидеть предыдущие версии, посмотрите историю изменений .

Есть много чего еще в гольфе. Я медленно работаю над этим.

С zlib / base64 получаем 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="))

Примечание: если вы видите, что мой счет повышается, возможно, это связано с тем, что я нашел несколько ошибок

Sp3000
источник
больше проблем с кодом, чем гольф с кодом, но все же 4872 * 0,8 = 3897,6
Def
3

Common Lisp, 868 байт * 0,85 = 737,8

Это обман для реализации Lisp с Lisp? Здесь все еще много для оптимизации.

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

Распечатывает E в случае ошибки ввода. Образцы прогонов:

$ 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
источник
2
пока это не своего рода функция eval ...
Def
2

Хаскелл, 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

довольно хакерское решение. это сохраняет все в виде строк в виде, готовом к выводу - их типы можно отличить по первой букве - 0..9для чисел, (для списков tили fдля логических значений, и все остальное для функций.

для запуска используйте rфункцию.

гордый хаскеллер
источник