교대 통역사 작성


10

편집 : 당신 중 일부는 의심, 공식 통역사에 버그가 있었다 : 구성 순서 .가 반대로했다. 나는 두 가지 버전의 통역사가 있었고 여기에서 잘못된 것을 사용했습니다. 이 잘못된 버전에 대한 예제도 작성되었습니다. 리포지토리에서 인터프리터와 아래 예제를 수정했습니다. 에 대한 설명 >도 약간 모호했기 때문에 수정했습니다. 또한 너무 오래 걸리는 것에 대한 사과는 실생활에 사로 잡혔습니다.

EDIT2 : 통역사에게 구현에 버그가 있었는데 .그 예에 반영되었습니다 (정의되지 않은 행동에 의존했습니다). 이제 문제가 해결되었습니다.

소개

Shift 는 몇 년 전에 만든 난해한 기능 프로그래밍 언어이지만 오늘 출판되었습니다. 스택 기반이지만 Haskell과 같은 자동 카레가 있습니다.

사양

Shift에는 두 가지 데이터 유형이 있습니다.

  • 임의의 양수가 함수, 인수에 대응 (입력의 수), 및 출력의리스트를 반환한다. 예를 들어, 유일한 입력을 복제하는 함수에는 arity 1이 있고 두 입력을 바꾸는 함수에는 arity 2가 있습니다.
  • 공백은 모두 동일하며 기능이 아닌 것 외에 다른 목적이 없습니다.

Shift 프로그램은 0 개 이상의 명령으로 구성되며 각 명령 은 단일 ASCII 문자입니다. 총 8 개의 명령이 있습니다.

  • !( apply ) 는 스택에서 함수 f와 값 x을 팝 하고에 적용 f됩니다 x. farity 1이 있으면 f(x)스택의 맨 앞에 목록 이 추가됩니다. arity n > 1가있는 경우 새로운 2 (n-1)진 함수 g가 스택으로 푸시됩니다. 입력을 받아 리턴합니다 .x1,x2,...,xn-1f(x,x1,x2,...,xn-1)
  • ?( blank )는 블랭크를 스택에 밀어 넣습니다.
  • +( clone )은 입력을 복제하는 단항 함수를 스택에 푸시합니다. 모든 값 x이에 매핑됩니다 [x,x].
  • >( shift )는 n-ary 함수를 취하는 단항 함수를 스택에 푸시 하고 첫 번째 인수를 무시하고 나머지 인수 를 호출 하며 결과 앞에 붙는 1 진 함수 를 f반환합니다 . 예를 들어, 입력을 가져 와서 반환 하는 이진 함수입니다 .(n+1)gxfxshift(clone)a,b[a,b,b]
  • /( fork )는 세 개의 입력을 취하는 삼항 함수를 스택에 푸시 하고 공백 인 경우 a,b,c리턴 하고 그렇지 않은 [b]경우 리턴 합니다 .a[c]
  • $( call )은 함수 f와 값 을 표시하는 이진 함수를 스택에 푸시 하고 정확하게 x적용됩니다 .fx!
  • .( 체인 ) 스택을 팝 두 기능하는 이진 함수 푸시 fg기능 : 및 그들의 조성물 반환 h같은 인수에 대응을 보유 f하고, 일반적으로 그 입력을 얻어 적용되는 f그들하고 완전히 적용 g(호출 결과에 f결과에서 남은 결과물에서 사용되지 않은 항목 이 포함 된 경우) h. 예를 들어, 즉 가정 f의 두 번째 인수를 클론, 이진 함수 g입니다 전화 . 스택에 포함되어 [f,g,a,b,c]있고 포함되어 있으면 .!!포함됩니다 [chain(f,g),a,b,c]. 우리가 할 경우 !!, 다음, 다음 f첫번째에 적용되는 a,b생산,[a,b,b]그런 다음 garity가 2이기 때문에 생산의 첫 번째 두 요소에 적용되며 [a(b),b]스택은 마침내됩니다 [a(b),b,c].
  • @( say )는 단순히 입력을 반환하는 단항 함수를 푸시하고 0공백 이거나 함수 인 경우 인쇄 1합니다.

!단순히 값을 스택으로 푸시하는 것을 제외한 모든 명령 은 입력을 수행 할 수있는 방법이 없으며 출력하는 유일한 방법은을 사용하는 것 @입니다. 프로그램은 명령을 하나씩 평가하고 "say"가 호출 될 때마다 0s 또는 1s를 인쇄 하고 종료 하여 해석됩니다 . 여기에 설명되지 않은 동작 (공백 적용, 길이가 0 또는 1 인 스택 적용, 빈에서 "체인"호출 등)은 정의되지 않습니다. 인터프리터가 충돌하거나, 자동으로 실패하거나, 입력을 요청하는 등의 작업이 수행 될 수 있습니다.

작업

당신의 임무는 Shift에 대한 통역을 작성하는 것입니다. STDIN, 명령 행 또는 함수 인수에서 해석 할 Shift 프로그램을 가져 와서 STDOUT으로 인쇄하거나 0s 및 1s 의 결과 (무한) 출력을 리턴해야합니다 . 함수를 작성하면 어떤 식 으로든 무한 길이 출력에 액세스 할 수 있어야합니다 (Python의 생성기, Haskell의 지연 목록 등). 또는 다른 입력 인 숫자를 가져 와서 보다 긴 경우 출력의 문자를 n리턴 할 수 있습니다 .nn

가장 낮은 바이트 수가 이기고 표준 허점이 허용되지 않습니다.

테스트 사례

이 Shift 프로그램은 01다음을 인쇄합니다 .

?@!@@!

왼쪽에서 시작 : 밀어 빈, 푸시 말은 다음에 적용 말을 빈에. 이 출력 0됩니다. 그런 다음 두 번 말하고 두 번째 을 첫 번째 에 적용하십시오 . 이 출력 1됩니다.

이 프로그램은 영원히 반복되므로 출력이 없습니다.

$+.!!+!!

푸시 전화클론은 , 다음 적용 체인 (우리가이 필요한 그들에게를 !이후의를 체인 진 기능입니다). 이제 스택에는 하나의 인수를 가져 와서 복제하고 두 번째에서 첫 번째 사본을 호출하는 함수가 포함됩니다. 을 사용 +!!하여이 함수를 복제하고 자체적으로 호출합니다.

이 프로그램은 0010다음을 인쇄합니다 .

?@$.++>!.!!.!!.!!!!+?/!!!@!@>!!!

빈칸을 밀고 라고 말합니다 . 그런 다음 두 번째 인수를 복사하는 이진 함수를 작성한 b다음 첫 번째 인수 를 복사하여 a자체 로 작성한 다음 컴포지션을의 사본에 적용하여를 b리턴 [a(a(b)),b]합니다. 이에 적용 말을 빈 후 적용 말을 스택에 남아있는 두 가지 요소에.

이 프로그램은 인쇄합니다 0. !!!추가 한 각각 에 대해 추가를 인쇄합니다 0.

?@+$>!>!+>!///!!>!>!.!!.!!.!!+!!!!

빈칸을 밀고 라고 말합니다 . 그런 다음 f,g,x입력으로 사용하고를 반환 하는 삼항 함수를 작성하십시오 [f,f,g,g(x)]. 복제 기능 자체에 적용, 그 과 빈. 이 애플리케이션은 스택을 변경하지 않으므로 원하는만큼 함수를 다시 적용 할 수 있습니다.

이 프로그램 001011011101111...1s 의 수가 항상 1 씩 증가 하는 무한 시퀀스를 인쇄합니다 .

@?/!@>!??/!!>!+.!!.!!.!!.+>!.!!$$$$+$>!>!$>!>!+>!$>!>!>!+>!>!///!!>!>!>!.!!.!!.!!.!!.!!.!!.!!.!!.!!.!!+!!!!!

저장소에는 주석이 달린 버전이 있습니다.


나는 약간 혼란 스러워요. shift 명령과 같이 "takes in"을 쓸 때, pop을 의미합니까, 아니면 apply 명령에 의해 적용되는 것을 의미합니까?
tecywiz121

1
또한 체인이 어떻게 작동 해야하는지 귀하의 사양에서 확실하지 않습니다. 예를 들어 설명해 주시겠습니까?
tecywiz121

당신은 스택의 상단에 두 가지 기능을 가지고, 한 말 : @ tecywiz121 이것은 내가 그것을 이해하는 방법이다 f(x1, x2, ..., xn)하고 g(y1, y2, ..., ym). 호출 .하면 둘 다 팝업되고 함수를 푸시합니다 h(z1, z2, ..., zn). 이제 당신은 점진적으로 카레를함으로써 모든 논쟁을 먹을 수 있습니다 !. 후 n응용 프로그램, 나머지 기능은 하나 개의 인자를 가지고 있었고, 그 시점에서 그것은 계산 f(z1, z2, ..., zn)(즉, f새로운 값을 밀어 후 즉시 소비, 당신이 카레 모든 인수에 적용) m스택의 값과 통화를 g그들.
Martin Ender

@ MartinBüttner Zgarb가 규칙과 일치한다고 생각하면 최대 출력 크기를 정의하는 두 번째 입력 매개 변수를 사용할 수 있습니다. 이것은 또한 게으른 평가 문제에 대한 해결책이 될 것입니다.
randomra

@ tecywiz121 .은 값 f보다 작은 목록을 반환 m하면 결과가 정의되지 않습니다 (구성에 arity n가 있으므로 스택에서 더 많은 인수를 사용할 수 없음)를 제외하고는 Martin이 설명한대로 정확하게 작동합니다 . 기본적으로의 출력은 f임시 스택으로 사용되며, 여기서는를 사용하여 g푸시 및 적용 되고 그 결과는 기본 스택에 추가됩니다. m!
Zgarb

답변:


12

파이썬 2 752 667 534 506 445 436 427 404 398 393 바이트

이것은 결코 짧지 않지만 ... 나는 최선을 다했습니다. 모든 골프 제안은 대단히 감사하겠습니다 ...

EDIT6 : 이것은 이제 함수 대신 스크립트입니다. 파일 (shift.py, forex)에 저장 한 다음로 실행하십시오 $ python shift.py '<my_input>'. 입력을 작은 따옴표로 묶어야합니다. 그렇지 않으면 bash가 입력 문자에 열중합니다.

EDIT7 : Aaaaaaand ... 더 이상 읽을 수 없습니다. 그러나 23 바이트를 더 제거했기 때문에 좋습니다. 언 골프 버전도 게시하겠습니다.

EDIT8 : @Zgarb 덕분에 골프 하나 더.

k,d=[],[]
u=k.append
def z(f,a=1):f.a=a;return f
exec "i=!x:x(*map(k.pop,[-1]*x.a)));e=dict(zip('?+>/$.@',[0,!x:u(x)<u(x)),!x:u(!a,*_:x(*_)<u(a),x.a+1))),!x,y,z:u((z,y)[x<1]),3),!x,y:u(!*_:x(y,*_),x.a-1))if x.a>1 else x(y),2),!x,y:u(!*_:x(*_)<i(y),x.a)),2),!x:d.append(`+(x>0)`)<u(x))]))".replace('!',"z(lambda ")
for _ in raw_input():
 try:[i,u][_ in e](e.get(_,e['$']))
 except:break
print d

편집 : 골프 도움말에 @DLosc 감사합니다! 85 바이트를 줄 이도록 관리되었습니다.

EDIT2 : 불필요한 래퍼를 잘라 내고 133 바이트 더 떨어 뜨립니다!

EDIT3 : ... 채팅에서 @ Sp3000 및 @orlp 덕분에 28 개 더!

EDIT4 : @orlp & @ Sp3000의 도움으로 모든 데코레이터를 제거했으며 이제 61 바이트가 짧습니다.

EDIT5 : 도와주세요, 나는 이것을 멈출 수 없습니다 .... 9 바이트가 더 사라졌습니다. 마지막 print 문을 제거하면 또 다른 7이 저장되지만 루프에서 m ()을 실행하면 모든 출력이 같은 줄에 있습니다 ... 괜찮습니까?

ungolfed 버전은 다음과 같습니다.

stack = []
push = stack.append

def arity(func,a=1): #give each of our functions an arity
    func.arity = a
    return func

def do(func): ##pop the args off the stack, then call the function
    args = map(stack.pop,[-1]*func.arity)
    func(*args)

def call(func,arg): #apply is just do(call)
    if func.arity == 1:
        func(arg)
    else:
        def curried(*a): #a quick little currier
            func(arg, *a)
        curried = arity(curried, func.arity - 1)
        push(curried)

def clone(arg):
    push(arg)
    push(arg)

def shift(func):
    def shifted(a, *arg):
        func(*arg)
        push(a)
    shifted = arity(shifted, func.arity + 1)
    push(shifted)

def fork(a, b, c):
    if a == 0:
        push(b)
    else:
        push(c)

def chain(func, gunc):
    def composition(*args):
        func(*args)
        do(gunc)
    composition = arity(composition, func.arity)
    push(composition)

def say(arg):
    print '10'[arg == 0],
    push(arg)

commands = {'?': 0,
            '+': arity(clone),
            '>': arity(shift),
            '/': arity(fork, 3),
            '$': arity(call, 2),
            '.': arity(chain, 2),
            '@': arity(say)}

def interpret(input_string):
    for command in input_string:
        try:
            if command == '!':
                do(call)
            else:
                push(commands[command])
        except RuntimeError: #this handles max recursion depth errors
            break            # for infinite programs
    print

if __name__ == "__main__":
    interpret(raw_input())

기본 아이디어는 파이썬 목록이 스택으로 매우 잘 작동하고 u=k.append저장하여 문자를 저장 할뿐만 아니라 @u함수를 푸시하기위한 데코레이터로 사용할 수도 있다는 것입니다 (더 이상은 아닙니다!).

n-arity의 함수에 작용하는 몇 가지 함수는 임의의 수의 인수를 허용 할 수 있어야하므로 *argsf.func_code.co_argcount를 추적하는 원래 계획이 arity로 대체되어야 함을 의미했습니다. 데코레이터 속성.

무한한 프로그램을 처리하는 관점에서, 인터프리터는 최대 재귀 깊이에 도달 할 때까지 실행됩니다. 맨 아래의 RuntimeError 핸들러는 해당 지점에서 조용히 종료되고 현재 출력 문자열을 인쇄합니다.

테스트 사례 :

>>> tests
['?@!@@!', '$+.!!+!!', '?@$..!!+.!!+>!.!!!!+?/!!!@!@>!!!', '?@+$>!>!.!!+>!.!!///!!>!>!.!!+!!!!', '?@+$>!>!.!!+>!.!!///!!>!>!.!!+!!!!!!!', '?@+$>!>!.!!+>!.!!///!!>!>!.!!+!!!!!!!!!!', '?@+$>!>!.!!+>!.!!///!!>!>!.!!+!!!!!!!!!!!!!', '@?/!@>!.!!??/!!>!.!!+.!!.+>!.!!$$.!!$.!!$.!!+.!!$>!>!.!!$>!>!.!!+>!.!!$>!>!>!.!!+>!>!.!!///!!>!>!>!.!!+!!!!!']
>>> for t in tests: m(t)
0 1

0 0 1 0
0
0 0
0 0 0
0 0 0 0
0 0 1 0 1 1 0 1 1 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

1
나의 첫 번째 반응 : @ _ @ 진심으로, 훌륭한 작업-실제 기능을 스택에 넣는 것은 정말 깔끔한 해결책입니다. 몇 가지 팁 : 1) 3 차 연산자는 일반적으로 단축 될 수 있습니다 . 2) 당신은 ['1','0'][...]다만으로 대체 할 수 있습니다 '10'[...]. 3) 왜 x is 0그렇지 않습니까 x==0(또는 x<1)? 4) 지정 귀찮게하지 마십시오 RuntimeError그냥 except할 것입니다. 5) Python 2를 사용하기 때문에 탭과 공백은 서로 다른 들여 쓰기 수준으로 계산되지만 ~ 25 바이트를 절약해야합니다.
DLosc

1
당신은 그것을 절단 할 수 있어야한다 x.a==1and x(y)or u(a(x.a-1)(b.partial(x,y)))고마웠다 논리 연산자 여전히 단락됩니다 만 원보다 적은 문자를 사용합니다. 그런 다음 x.a-1조건 으로 사용하여 (0 / false이면 x1, 0이 아닌 경우 true) 'then'및 'else'표현식을 바꾸어 다른 바이트를 저장 하십시오 x.a-1and u(a(x.a-1)(b.partial(x,y)))or x(y). (당신이 나를 통과 한 지금 골프 광산을 좀 더
가져와야한다

1
내 것과 비슷한 문제를 겪은 후 지금 실패한 것을 이해 x.a==1합니다. 사실이지만 x(y)허위를 반환하면 평가하려고 시도합니다 u(...). 그러나 주어진 3 바이트를 저장할 필요가없는 것처럼 보입니다! 인정합니다. 선생님이 저를 능가하셨습니다.
DLosc

1
한 번의 퀴즈 : 지정된 출력 형식에 공백이 없습니다 . 어떤 전략 이 가장 짧은 지 확실하지 않은 다양한 전략으로 해결할 수 있습니다 . 물론, 프로그램 RuntimeError은 사용자가 stderr를 리디렉션하도록 요청 하는 동안 처리합니다 . 그래서 우리는 아마도 혼란에 빠질 것입니다. ; ^)
DLosc

1
*_람다에서 무엇입니까 ?
mbomb007

4

고스트 스크립트

아직 파싱 기능을 해결해야하기 때문에 아직 골프를 치지 않았습니다.

이 구현 사용 _:대신 >하고 /, 그리고 공백으로 구분되는 모든 프로그램의 문자를 필요로한다. 때문입니다 >/포스트 스크립트에 유효한 이름이 아니며, 사업자는 자기의 경계 설정하지 않은,하지만 난 파서를 쓸 때이 해결 될 것입니다.

코드의 첫 부분은 단순히 연산자 함수의 정의를 반복하기 때문에 상당히 투명해야합니다. 의 정의에서 마술이 발생합니다 !.

/switch {
    /y exch def
    /x exch def
    {x} {y} ifelse
} bind def

/unwrap {
    dup type (arraytype) eq {aload pop} if
} bind def

/! { % IN: <x> <f> OUT: <g>|<f(x)>
    [ 3 1 roll unwrap] cvx %prefix argument into function
    dup /fun exch def %bind

    [ count 1 roll ] { %run the function sandboxed so it can't take any additional args
        2 dict begin
        /=only {} def % suppress output
            {
                fun
            } stopped /err exch def clear err
        end
    } .runandhide


    exch {
        $error /errorname get
        (stackunderflow) ne {
            handleerror
        } if

        $error /newerror false put

        unwrap
    } {
        unwrap exec
    } ifelse
} def

/? 0 def
/+ {{dup}} def
/_ {{/f exch def pop f}} def % using _ instead of >
/: {{? ne 3 1 roll switch}} def % using : instead of /
/$ {{!}} def
/. {{/g exch def exec g}} def 
/@ {{dup ? eq {0}{1} ifelse =only}} def

방법의 !작품은 간단하다 : 첫째, 그것은 인수를 추가 xf앞에 붙여 x의 내용을 f다시 스택에 밀어, 그 결과의 사본을 명명 fun.

그런 다음 전체 스택을 배열로 마무리합니다. .runandhideA는 고스트 스크립트 확장 , 샌드 박스 코드를 실행하는가에 대한 호출 과정에서 앞의 배열의 내용을 숨기는. 이 dict명령은 dict 스택에서 새 사전을 푸시하여 end다시 정의 될 때까지 정의 된 이름 범위를 좁 힙니다 . 또한 =only(에서 사용하는 출력 연산자 @)를 더미로 대체 하여 시험 실행 중에 출력을 억제합니다. stoppedtry다른 언어에서 발견 된 명령문 과 동등한 PostScript이며, 프로 시저에서 오류가 발생하면 true를 리턴하고 완료된 경우 false를 리턴합니다.

평가판 실행이 fun완료되면 프로그램은 숨겨진 어레이에서 원래 스택을 복원하고 fun오류없이 완료된 경우 실제로 스택 을 실행하여 출력을 유지합니다.


2

Python3, 685 670 634 633 바이트

나는 이것이 내가 이제까지 골프를 본 가장 긴 것이라고 확신합니다. 그것은 다소 읽을 수 있었지만 @sirpercival의 조언을 따르면 그 단점 을 제거 했습니다 !

from re import*
E,*k="E"
P="e(k.pop(),k.pop())"
def H(a,b):global k;k+=list(a)+[N(b)];exec("k+=%s;"%P*Z(N(b)));return[]
def e(a,b):a=sub("(?<!\d)0",repr(N(b,1)).replace("\\",r"\\"),a,1);return Z(a)and[a]or list(eval(a))
D=list(zip("ilhydsSNZ",[3,2,2]+[1]*6,sub("A","N(a)",',b,c:[N([b,c][a>E])]|,b:e(A,N(b))|,b:["H(%s,%s)"%(A,repr(b))]|:print(0+(a>E),end="")or[A]|:[A]*2|:["S(0,%s)"%A]|,b:b+[A]|,b=-1:sub("\d+",lambda m:str(int(m.group())+b),a)|:len(split("\D0",a))-1').split("|")))
for n,r,f in D:exec(n+"=lambda a"+f)
F=dict(zip("/$.@+>?!",D))
for z in input():n,r,f=F[z];k+=z!="!"and[[n+"(%s)"%",".join("0"*r),E][z=="?"]]or eval(P)

k스택 "h(0,0)"은 ( c h ain 과 같은) 문자열로 표시되는 함수를 포함합니다 . 함수가 다른 함수에 인수로 전달되면 repr'd'가 표시되고 모든 숫자가 증가 "h('h(1,1)',0)"합니다. 모든 0s가 함수로 대체 되면 전체가로 전달되어 eval적절한 Python 함수를 호출합니다. 대부분 6 번째 줄의 큰 문자열에서 exec7 번째 줄까지 생성 된 람다 함수 입니다.

여러 수준의 중첩 함수를 증가시키고 인용하고 올바르게 이스케이프 처리하는 것이 가장 큰 두통이었습니다. 함수 중첩이 9 레벨보다 깊게 진행되지 않는다고 가정 할 수 있지만 아마도 안전한 가정이 아닌 주석에서 지적한 것처럼 정규 표현식 작업에 조금 더 절약 할 수 있습니다.

Ungolfed 이전 버전의 코드 :

from re import *
E="E"
stack=[]

clone=lambda a:[unnest(a)]*2
shift=lambda a:["shifted(0,%s)"%unnest(a)]
fork=lambda a,b,c:[unnest(c if a!=E else b)]
call=lambda a,b:apply(unnest(a),unnest(b))
chain=lambda a,b:["chained(%s,%s)"%(unnest(a),repr(b))]
def say(a):
 print(1 if a!=E else 0,end="")
 return [unnest(a)]

shifted=lambda a,b:b+[unnest(a)]
def chained(a,b):
 global stack
 stack+=list(a)+[unnest(b)]
 exec("stack+=apply(stack.pop(),stack.pop());"*zeros(unnest(b)))
 return []

nest=lambda a,direction:sub("\d+",lambda m:str(int(m.group())+direction),a)
unnest=lambda a:nest(a,-1)
zeros=lambda a:len(split("\D0",a))-1
def apply(a,b):
 a=sub("(?<!\d)0",repr(nest(b,1)).replace("\\",r"\\"),a,1)
 return [a] if zeros(a) else list(eval(a))

functions=dict(zip("+>/$.@",zip(["clone","shift","fork","call","chain","say"],[1,1,3,2,2,1])))

for cmd in input():
 if"!"==cmd:
  stack+=apply(stack.pop(),stack.pop())
 elif"?"==cmd:
  stack+=[E]
 else:
  name,arity=functions[cmd]
  stack+=[name+"(%s)"%",".join("0"*arity)]

이 구현의 잠재적 인 단점 중 하나는 재귀를 사용하므로 무한해야하는 프로그램이 최대 재귀 깊이에 매우 빨리 도달한다는 것입니다. (무한 프로그램을 실행할 때 stderr를 리디렉션하고 싶을 수도 있습니다. 그렇지 않으면 스택 추적이 실제 출력을 엉망으로 만듭니다.) 그 외에는 모든 것이 작동하는 것 같습니다.


위의 프로그램을 생성하고 실행하는 프로그램을 작성할 수 있습니까? lambda aand 처럼 압축 가능한 반복 코드가 많이 있습니다 k.pop().
mbomb007

@ mbomb007 ... 내 뇌가 폭발 할 것 같아. (그러나 최근의 편집을 참조하십시오- k.pop()어쨌든 상황을 조금 덜 반복적으로 만들었 습니다.)
DLosc

모든 람다에 대해 exec / translate 트릭을 수행 할 수 있습니까? 한 줄에 모두 붙이세요?
sirpercival

다른 의견 :이 언어를 사용하여 함수 중첩 <= 9에 의존 할 수 있을지 의심됩니다.
sirpercival

@ sirpercival 예, 나는 그것을 시도하려고 생각했습니다. 그리고 아닙니다. : ^ P
DLosc

1

실론, 1167 1057 1031

모노 타입 파이썬 버전만큼 짧지는 않습니다 ...

import ceylon.language.meta.model{N=Function}import ceylon.collection{H=HashMap}interface D of F|b{}object b satisfies D{}class F(shared Integer a,[D+](D+)f,[D*]c=[])satisfies D{shared[D+]o(D i){[D+]s=[i].prepend(c);return a==1then f(*s)else[F(a-1,f,s)];}shared[D+]y([D+]i){return f(*i.prepend(c));}}F m<A>(N<[D+],A>f)given A satisfies[D+]=>F(f.parameterTypes.size,(D+i)=>f.apply(*i));[D,D]e(D x)=>[x,x];[F]t(F f){[D+]g(D+i){assert(is[D+]r=i.rest);return[i[0],*f.y(r)];}return[F(f.a+1,g)];}[D]k(D a,D d,D c)=>a==b then[d]else[c];[D+]l(F a,D x)=>a.o(x);[F]n(F f,F g){[D+]h(D+i){[D+]r=f.y(i);assert(is[D+]d=r[0:g.a]);return g.y(d).append(r[g.a...]);}return[F(f.a,h)];}[D]y(D x){process.write(x==b then"0"else"1");return[x];}class I(){variable D[]s=[];value c=H{'?'->b,'+'->m(`e`),'>'->m(`t`),'/'->m(`k`),'$'->m(`l`),'.'->m(`n`),'@'->m(`y`)};shared void r(Character i){if(i=='!'){assert(is F f=s[0],is D x=s[1]);s=f.o(x).append(s[2...]);}else{assert(is D d=c[i]);s=[d].append(s);}}}shared void z(){process.readLine()?.collect(I().r);}

다음은 동일한 코드의 형식화 및 주석 처리 된 버전입니다 (공백 / 줄 바꾸기 / 설명은 4867 바이트가 됨).

import ceylon.language.meta.model {
    N=Function
}
import ceylon.collection {
    H=HashMap
}
//↑ Import of stuff we need – with a shorter alias.
// (The comment is down here due to a bug in my comment and space
//  remover – it doesn't remove a comment if it is the first token
//  at all.)

// Our data items are either functions or blanks.
interface D of F | b {}

// There is no point in having many blanks – so here a singleton.
object b satisfies D {}

// The function class. Our functions take a number of data items,
// and return a number of data items.
// We know the arity a, and have also an actual function f, and a number
// or already collected arguments.
class F(shared Integer a, [D+](D+) f, [D*] c = [])
        satisfies D {
    // apply once (= collect one parameter). Returns either the result,
    // or a function with arity one less.
    shared [D+] o(D i) {
        [D+] s = [i].prepend(c);
        return a == 1 then f(*s) else [F(a - 1, f, s)];
    }
    // apply fully (= with all needed parameters).
    // The input size should equal the arity.
    shared [D+] y([D+] i) {
        // merge collected and input arguments.
        return f(*i.prepend(c));
    }
}
// creates a shift function from a ceylon function,
// deriving the arity using reflection.
F m<A>(N<[D+],A> f)
        given A satisfies [D+]
        => F(f.parameterTypes.size, (D+ i) => f.apply(*i));

//
// clone: a unary function that duplicates its input: any value x is mapped to [x,x].
//
[D, D] e(D x) => [x, x];

//
// shift: a unary function that takes in an n-ary function f, and returns an
// (n+1)-ary function g that ignores its first argument x, calls f on the
// remaining ones, and tacks x in front of the result. For example,
// shift(clone) is a binary function that takes inputs a,b and returns [a,b,b].
//
[F] t(F f) {
    [D+] g(D+ i) {
        assert (is [D+] r = i.rest);
        return [i[0], *f.y(r)];
    }
    return [F(f.a + 1, g)];
}

//
// fork: a ternary function that takes three inputs a,d,c, and returns [d] if a is a blank,
// and [c] otherwise.
//
[D] k(D a, D d, D c) => a == b then [d] else [c];

//
// call: a binary function that pops a function f and a value x,
//        and applies f to x exactly as ! does.
//
[D+] l(F a, D x) => a.o(x);

//
// chain:  a binary function that pops two functions f and g, and returns their composition:
//         a function h that has the same arity as f, and which takes its inputs normally, applies
//         f to them, and then fully applies g to the result (calls it as many times as its arity
//         dictates), with unused items from the output of f remaining in the result of h. For
//         example, suppose that f is a binary function that clones its second argument, and
//         g is call. If the stack contains [f,g,a,b,c] and we do .!!, then it contains
//         [chain(f,g),a,b,c]; if we do !! next, then f is first applied to a,b, producing
//         [a,b,b], then g is applied to the first two elements of that since its arity is 2,
//         producing [a(b),b], and the stack will finally be [a(b),b,c].
//
[F] n(F f, F g) {
    [D+] h(D+ i) {
        // call f, remember the results.
        [D+] r = f.y(i);
        // first some results from f are the arguments to g:
        assert (is [D+] d = r[0:g.a]);
        // remaining results from f are passed back directly, with the results from g.
        return g.y(d).append(r[g.a...]);
    }
    return [F(f.a, h)];
}

//
// say: a unary function that simply returns its input, and prints 0 if it was a blank,
//      and 1 if it was a function.
// 
[D] y(D x) {
    process.write(x == b then "0" else "1");
    return [x];
}

//
// Interpreter class, which manages the stack and interprets the commands.
// Just call the r method with the individual command characters.
//
class I() {
    // The stack. The only variable in the whole program.
    variable D[] s = [];

    // a hash map of items to be pushed by commands, most build using the m function.
    // The apply command is not here, this is handled separately by the interpreter. 
    value c = H {
        '?'->b,
        '+'->m(`e`),
        '>'->m(`t`),
        '/'->m(`k`),
        '$'->m(`l`),
        '.'->m(`n`),
        '@'->m(`y`)
    };

    // Interprets one command, indicated by a character.
    // Will throw an AssertionError for unknown commands.
    shared void r(Character i) {
        if (i == '!') {
            assert (
                is F f = s[0],
                is D x = s[1]);
            // apply f on x, push the result onto a shortened version of the stack.
            s = f.o(x).append(s[2...]);
        } else {
            assert (is D d = c[i]);
            // push d on top of the stack.
            s = [d].append(s);
        }
    }
}

shared void z() {
    process.readLine()?.collect(I().r);
}

함수 clone e, shift t, fork k, call l, sayy 및 chainn 는 약어 버전의 이름의 마지막 문자를 사용합니다. 충돌이 적기 때문입니다. (하찮은 일 : 포크는 원래 이런 식으로 정의 하였다 : [Data] fork(Data a, Data b, Data c) => a == blank then [b] else [c];- 나는 이름을 바꿀 경우 blankb,이 파산, 지금 매개 변수를 비교하기 때문에 a하고 b대신 a빈으로 나에게 디버깅 시간이 걸렸다..)

z기능은 내 IDE가 그 기능을 실행하기 때문에 공유 - 명령 행 도구는 공유되지 않는 것들을 실행할 수 있습니다.


루핑 버전은 실제로 어떤 시점에서 StackOverflowError를 던지고 마무리합니다. JVM에는 재귀 스택 최적화가 없거나 최소한 내 프로그램에서 작동하지 않습니다.
Paŭlo Ebermann
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.