Це майже Лісп!


14

Виклик

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

(func arg1 arg2 ...)

Зауважте, що інтерпретатор повинен передбачати сторонні символи пробілу до та після дужок, функцій та аргументів.

Типи

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

Функції

Для впровадження необхідні наступні функції, які знаходяться у форматі Функція , Артіті . Якщо Arity nпереходить зі знаком плюс, то це позначає nабо більше аргументів. Ви можете припустити, що всі аргументи, надані функції, є одного типу, якщо не вказано інше. Ви також можете припустити, що якщо для сертифікованого типу не вказано жодної поведінки, то ви можете припустити, що жоден аргумент цієї функції ніколи не буде такого типу. Аргументи будуть посилатися на наступну діаграму:

(func argument1 argument2 ... argumentn)

  • + , 2+

    • Якщо всі аргументи мають тип Integer , потрібно повернути суму аргументів
    • Якщо всі аргументи мають список List , ви повинні повернути конкатенацію аргументів у порядку зростання ( arg1+arg2+ ...)
    • Якщо всі аргументи мають тип булевого типу , ви повинні повернути логічну всю послідовність аргументів
    • (+ 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- ...)
    • Якщо всі аргументи мають тип булевого типу , ви повинні повернути логічну Будь-яку з послідовностей аргументів
    • (- 8 4 3) -> 1
    • (- 0 123) -> -123
    • (- true false false true false) -> true
  • * , 2+

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

    • Якщо всі аргументи типу Integer , ви повинні повернути коефіцієнт аргументів ( arg/arg2/ ...) (ви можете припустити, що ділення виконується послідовно, і що десяткова частина на кожному кроці усікається)
    • Якщо один аргумент має тип List, а другий має функцію типу , тоді ви повинні повернути отриманий список після того, arg2як буде відображено кожне значення
    • (/ 100 10 3) -> 3
    • (/ (list 1 2 3) inc) -> (list 2 3 4)
  • % , 2

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

    • Якщо і тип, і значення всіх аргументів однакові, ви повинні повернути true. В іншому випадку поверніть хибне.
    • (= 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 , ви повинні повернути ціле число зменшене на одиницю
    • Якщо аргумент типу List , ви повинні повернути список, повернутий проти годинникової стрілки, одним обертанням
    • (dec 1) -> 0
    • (dec (list 1 2 3)) -> (list 2 3 1)
  • якщо , 3

    • Якщо дано три аргументи будь-якого типу: Якщо значення істини arg1є істинним, поверніть arg2, інакше повернітьarg3
    • (if (not (list 1)) 8 false) -> false
  • ні , 1

    • Якщо вам дано аргумент будь-якого типу, якщо значення істинності arg1False, поверніть true, інакше поверніть false.
    • (not (list)) -> true
  • len , 1

    • Якщо вам дано аргумент типу List , поверніть довжинуarg1
    • (len (list 4 2 true (list 3) (list))) -> 5

Таблиця істини:, 0, (list), false -> falseде (list)позначається порожній список. Все інше є true.

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

Якщо вибирати перший, вихід для Integers - це просто числа, для 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якщо вони не повернуться

Це код-гольф, тому перекладач з найнижчою кількістю байтів виграє!


Як один інтерпретує (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)

b випадки, які 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

Пітон 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="))

Примітка. Якщо ви бачите, що мій показник збільшується, це, мабуть, тому, що я знайшов кілька помилок


виклик коду, ніж гольф коду, але все ж, 4872 * 0,8 = 3897,6
Def

3

Ліс звичайний, 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))

Виводить Е у разі помилки в введенні. Проби:

$ 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
до тих пір, поки це не є якась функція 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функцію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.