เขียน Shift Interpreter


10

แก้ไข:ในขณะที่บางคนสงสัยว่ามีข้อผิดพลาดในล่ามอย่างเป็นทางการ: ลำดับขององค์ประกอบใน.ถูกกลับรายการ ฉันมีล่ามสองรุ่นและใช้ผิดที่นี่ ตัวอย่างถูกเขียนขึ้นสำหรับรุ่นที่ไม่ถูกต้อง ฉันแก้ไขล่ามในที่เก็บและตัวอย่างด้านล่าง คำอธิบายของ>มันค่อนข้างคลุมเครือดังนั้นฉันจึงแก้ไขได้ นอกจากนี้ขอโทษสำหรับการใช้เวลานานมากฉันถูกจับในบางสิ่งในชีวิตจริง

แก้ไข 2:ล่ามของฉันมีข้อบกพร่องในการใช้งาน.ซึ่งสะท้อนให้เห็นในตัวอย่าง (พวกเขาอาศัยพฤติกรรมที่ไม่ได้กำหนด) ปัญหาได้รับการแก้ไขแล้ว

บทนำ

Shiftเป็นภาษาโปรแกรมฟังก์ชั่นลึกลับที่ฉันสร้างเมื่อสองสามปีก่อน แต่เผยแพร่ในวันนี้ มันเป็นแบบกองซ้อน แต่ก็มีการแกงอัตโนมัติเช่น Haskell

สเปค

มีสองประเภทข้อมูลใน Shift:

  • ฟังก์ชั่นซึ่งมีarityบวกโดยพลการ(จำนวนอินพุต) และส่งคืนรายการเอาต์พุต ตัวอย่างเช่นฟังก์ชั่นที่ซ้ำกันของอินพุตเพียงอย่างเดียวมี arity 1 และฟังก์ชั่นที่แลกเปลี่ยนสองอินพุตมี arity 2
  • ช่องว่างซึ่งเหมือนกันหมดและไม่มีวัตถุประสงค์อื่นนอกเหนือจากการไม่ทำหน้าที่

โปรแกรม Shift ประกอบด้วยคำสั่งเป็นศูนย์หรือมากกว่าซึ่งแต่ละคำสั่งเป็นอักขระ ASCII เดียว มีทั้งหมด 8 คำสั่ง:

  • !( ใช้ ) ปรากฏฟังก์ชั่นfและคุ้มค่าxจากสแต็คและนำไปใช้ในการf xหากfมี arity 1 รายการf(x)จะถูกเพิ่มที่ด้านหน้าของสแต็ก ถ้ามันมี arity ฟังก์ชัน -ary n > 1ใหม่จะถูกส่งไปยังสแต็ก มันต้องใช้ปัจจัยการผลิตและผลตอบแทน(n-1)gx1,x2,...,xn-1f(x,x1,x2,...,xn-1)
  • ?( ว่าง ) ผลักว่างไปยังสแต็ก
  • +( โคลน ) เพื่อผลักดันให้สแต็คฟังก์ชั่นเอกที่ซ้ำอินพุต: ค่าใด ๆถูกแมปไปx[x,x]
  • >( shift ) ส่งไปยังสแต็กฟังก์ชัน unary ที่เกิดขึ้นในnฟังก์ชัน -ary fและส่งกลับ(n+1)ฟังก์ชัน -ary gที่ละเว้นอาร์กิวเมนต์แรกxเรียกใช้ฟังก์ชันที่fเหลืออยู่และ tacks ที่xด้านหน้าของผลลัพธ์ ยกตัวอย่างเช่นshift(clone)เป็นฟังก์ชั่นไบนารีที่ใช้ปัจจัยการผลิตและผลตอบแทนa,b[a,b,b]
  • /( fork ) ส่งไปยังสแต็กของฟังก์ชันประกอบไปด้วยที่รับสามอินพุตa,b,cและส่งคืน[b]ถ้าaว่างเปล่าและ[c]อื่น ๆ
  • $( เรียก ) ผลักไปที่กองซ้อนฟังก์ชั่นไบนารีที่ปรากฏฟังก์ชั่นfและค่าxและนำfไปใช้กับxเช่นเดียวกับที่!ทำ
  • .( เชน ) ผลักไปที่สแต็กฟังก์ชันเลขฐานสองที่ปรากฏสองฟังก์ชันfและgและคืนค่าการจัดองค์ประกอบ: ฟังก์ชั่นhที่มี arity เดียวกับfและที่รับอินพุตตามปกติจะใช้fกับพวกเขาแล้วนำไปใช้กับผลลัพธ์อย่างเต็มที่gมันหลาย ๆ ครั้งตามคำสั่งของ arity) กับรายการที่ไม่ได้ใช้จากการส่งออกของที่เหลืออยู่ในผลมาจากการf hตัวอย่างเช่นสมมติว่าfเป็นหน้าที่ไบนารีที่โคลนอาร์กิวเมนต์ที่สองของตนและgเป็นโทร หากสแต็คมี[f,g,a,b,c]และเราทำ.!!แล้วมันมี[chain(f,g),a,b,c]; ถ้าเราทำ!!ต่อไปก็fจะถูกนำไปใช้กับa,bการผลิตครั้งแรก[a,b,b]แล้วgถูกนำไปใช้สององค์ประกอบแรกของว่าตั้งแต่ arity ของมันคือ 2, การผลิตและสแต็คในที่สุดก็จะเป็น[a(b),b][a(b),b,c]
  • @( พูด ) ผลักดันฟังก์ชั่นยูนารีเพียงแค่คืนค่าอินพุตและพิมพ์0ถ้ามันว่างเปล่าและ1ถ้ามันเป็นฟังก์ชั่น

โปรดทราบว่าคำสั่งทั้งหมดยกเว้นเพียงแค่ผลักดันมูลค่าให้กับกองมีวิธีการดำเนินการป้อนข้อมูลไม่และวิธีเดียวที่จะส่งออกอะไรคือการใช้งาน! @โปรแกรมจะถูกตีความโดยการประเมินคำสั่งทีละคนการพิมพ์0s หรือ1s เมื่อใดก็ตามที่ "พูด" เรียกว่าและออก พฤติกรรมใด ๆ ที่ไม่ได้อธิบายไว้ที่นี่ (การใช้ช่องว่างการใช้สแต็คความยาว 0 หรือ 1 การเรียก "chain" บนช่องว่าง ฯลฯ ) ไม่ได้กำหนดไว้: ล่ามอาจล้มเหลวล้มเหลวอย่างเงียบ ๆ ขอข้อมูลหรืออะไรก็ตาม

งาน

งานของคุณคือการเขียนล่ามสำหรับ Shift ควรใช้จาก STDIN บรรทัดคำสั่งหรือฟังก์ชันอาร์กิวเมนต์ที่โปรแกรม Shift ถูกตีความและพิมพ์ไปที่ STDOUT หรือส่งกลับผลลัพธ์ (อาจเป็นอนันต์) ของ0s และ1s หากคุณเขียนฟังก์ชั่นคุณจะต้องสามารถเข้าถึงเอาต์พุตที่มีความยาวไม่สิ้นสุดในบางวิธี (ตัวสร้างใน Python รายการสันหลังยาวใน Haskell ฯลฯ ) หรือคุณสามารถใช้การป้อนข้อมูลอีกจำนวนnและการกลับมาอย่างน้อยตัวละครของการส่งออกถ้ามันมีความยาวมากกว่าnn

จำนวนไบต์ต่ำสุดที่ชนะและช่องโหว่มาตรฐานไม่ได้รับอนุญาต

กรณีทดสอบ

โปรแกรม Shift นี้จัดพิมพ์01:

?@!@@!

เริ่มต้นจากทางซ้าย: ผลักที่ว่างเปล่าแล้วกดพูดแล้วใช้คำพูดนั้นกับที่ว่าง ผลลัพธ์0นี้ จากนั้นกดพูดสองครั้งและใช้คำพูดที่สองกับคำแรก ผลลัพธ์1นี้

โปรแกรมนี้วนซ้ำตลอดไปโดยไม่มีผลลัพธ์:

$+.!!+!!

กดcallและcloneจากนั้นใช้chainกับพวกมัน (เราต้องการสอง!s เนื่องจากchainเป็นฟังก์ชันไบนารี) ตอนนี้สแต็กมีฟังก์ชั่นที่รับหนึ่งอาร์กิวเมนต์ทำซ้ำมันและเรียกสำเนาแรกในวินาที ด้วย+!!เราทำซ้ำฟังก์ชั่นนี้และเรียกมันด้วยตัวเอง

โปรแกรมนี้พิมพ์0010:

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

ผลักดันว่างเปล่าและบอกว่า จากนั้นเขียนฟังก์ชั่นไบนารีที่สำเนาอาร์กิวเมนต์ที่สองbแล้วเล่มแรกaและประกอบด้วยมันด้วยตัวเองแล้วใช้องค์ประกอบในการสำเนาของกลับb [a(a(b)),b]ใช้เพื่อพูดและว่างเปล่าจากนั้นใช้พูดกับองค์ประกอบสองอย่างที่เหลืออยู่ในสแต็ก

0โปรแกรมนี้จะพิมพ์ สำหรับแต่ละที่คุณผนวกกับมันพิมพ์เพิ่มเติม!!!0

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

ผลักดันว่างเปล่าและบอกว่า จากนั้นเขียนฟังก์ชั่นไตรภาคที่จะเป็นปัจจัยการผลิตและผลตอบแทนf,g,x [f,f,g,g(x)]โคลนฟังก์ชันนั้นและนำไปใช้กับตัวเองพูดและช่องว่าง แอปพลิเคชันนี้ไม่เปลี่ยนสแต็กดังนั้นเราจึงสามารถใช้ฟังก์ชั่นอีกครั้งได้หลายครั้งตามที่เราต้องการ

โปรแกรมนี้พิมพ์ลำดับอนันต์001011011101111...โดยจำนวน1s เพิ่มขึ้นหนึ่งครั้งเสมอ:

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

ที่เก็บมีเวอร์ชันที่มีคำอธิบายประกอบ


ฉันสับสนเล็กน้อยที่นี่ เมื่อคุณเขียน "ใช้ใน" เช่นเดียวกับในคำสั่ง shift คุณหมายถึงปรากฏหรือคุณหมายถึงนำไปใช้โดยคำสั่งใช้?
tecywiz121

1
นอกจากนี้ฉันไม่แน่ใจจากสเป็คของคุณว่าโซ่ควรทำงานอย่างไร คุณช่วยอธิบายด้วยตัวอย่างได้ไหม
tecywiz121

@ tecywiz121 นี่คือวิธีที่ผมเข้าใจมันว่าคุณมีสองฟังก์ชั่นด้านบนของสแต็ค, และf(x1, x2, ..., xn) g(y1, y2, ..., ym)โทรปรากฏทั้งสองของพวกเขาและผลักดันให้ฟังก์ชั่น. ตอนนี้คุณสามารถกินได้ทุกข้อโต้แย้งเหล่านั้นโดยค่อยๆดีความชอบด้วยh(z1, z2, ..., zn) !หลังจากnแอปพลิเคชั่นดังกล่าวฟังก์ชั่นที่เหลือมีเพียงหนึ่งอาร์กิวเมนต์เท่านั้นและ ณ จุดนั้นจะคำนวณf(z1, z2, ..., zn)(เช่นfนำไปใช้กับอาร์กิวเมนต์ทั้งหมดที่คุณ curried) ซึ่งจะผลักดันค่าใหม่บางค่าจากนั้นจะใช้mค่าจากสแต็g
Martin Ender

@ MartinBüttnerหาก Zgarb คิดว่าเป็นไปตามกฎที่คุณสามารถใช้พารามิเตอร์อินพุตที่สองซึ่งกำหนดขนาดสูงสุดของเอาต์พุต นี่จะเป็นวิธีแก้ปัญหาการประเมินผลที่ขี้เกียจ
randomra

@ tecywiz121 .ทำงานตรงตามที่ Martin อธิบายยกเว้นว่าหากfส่งคืนรายการที่น้อยกว่าmค่าผลลัพธ์จะไม่ได้กำหนดไว้ (การจัดองค์ประกอบมี arity nดังนั้นจึงไม่สามารถกินอาร์กิวเมนต์เพิ่มเติมจากสแต็กได้) โดยพื้นฐานแล้วเอาต์พุตของfถูกใช้เป็นสแต็กชั่วคราวซึ่งgจะถูกผลักและใช้mเวลาโดยใช้!และผลลัพธ์ของสิ่งนั้นจะถูกเพิ่มเข้ากับสแต็กหลัก
Zgarb

คำตอบ:


12

Python 2, 752 667 534 506 445 436 427 404 398 393 ไบต์

นี่ไม่สั้นเลย ... แต่ฉันก็พยายามทำให้ดีที่สุด คำแนะนำการเล่นกอล์ฟใด ๆ ที่จะได้รับการชื่นชมอย่างมาก ...

EDIT6: ตอนนี้เป็นสคริปต์แทนฟังก์ชั่น บันทึกเป็นไฟล์ (shift.py อัตราแลกเปลี่ยน) $ python shift.py '<my_input>'แล้วใช้มันด้วย ตรวจสอบให้แน่ใจว่าใส่ข้อมูลในเครื่องหมายคำพูดเดี่ยวมิเช่นนั้น bash จะบ้าคลั่งไปกับอักขระอินพุต

EDIT7: อ่าาาาาา ... มันอ่านไม่ได้แล้ว แต่ฉันได้กำจัด 23 ไบต์ขึ้นไปดังนั้นก็ดีฉันเดาได้ไหม ฉันจะโพสต์เวอร์ชันที่ไม่ดี

แก้ไข 8: อีกหนึ่งสนามกอล์ฟต้องขอบคุณ @Zarb

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: ... และอีก 28 ขอบคุณ @ Sp3000 และ @ orlp ในการแชท!

EDIT4: ด้วยความช่วยเหลือจาก @orlp & @ Sp3000 นำเครื่องมือตกแต่งออกทั้งหมดและตอนนี้มันสั้นลง 61 ไบต์

EDIT5: ช่วย meeeeee ฉันหยุดเล่นกอล์ฟไม่ได้ .... อีก 9 ไบต์หายไป การกำจัดคำสั่งพิมพ์สุดท้ายจะช่วยประหยัดอีก 7 แต่ถ้าคุณรัน m () ในลูปเอาต์พุตทั้งหมดอยู่ในบรรทัดเดียวกัน ... ตกลงไหม?

นี่คือรุ่นที่ไม่ได้แต่งแต้ม:

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 จำเป็นต้องสามารถยอมรับจำนวนอาร์กิวเมนต์ได้ตามอำเภอใจฉันจึงต้องใช้*argsซึ่งหมายความว่าแผนดั้งเดิมของฉันในการติดตาม f.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) ผู้ประกอบการ Ternary สามารถมักจะสั้นลงหรืออีกวิธีหนึ่ง 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 / เท็จถ้าxเป็น 1 ภัณฑ์ / จริงอื่น ๆ ) และการแลกเปลี่ยน 'แล้ว' และสำนวน 'อื่น x.a-1and u(a(x.a-1)(b.partial(x,y)))or x(y)ๆ' (ต้องตีกอล์ฟเพิ่มอีกสักหน่อยตอนนี้คุณผ่านฉันไปแล้ว ... ; ^))
DLosc

1
หลังจากพบปัญหาที่คล้ายกันกับฉันฉันเข้าใจว่าสิ่งที่ล้มเหลวตอนนี้ - ถ้าx.a==1เป็นจริง แต่x(y)คืนสิ่งที่ผิดพลาดก็พยายามประเมินu(...)เช่นกัน แต่ดูเหมือนว่าคุณไม่จำเป็นต้องบันทึกข้อมูล 3 ไบต์ที่เลวทรามที่จะให้คุณ! ฉันยอมรับคุณ: คุณได้แซงฉัน
DLosc

1
เพียงหนึ่งการเล่นโวหาร: รูปแบบผลลัพธ์ที่ระบุไม่มีช่องว่าง - คุณสามารถแก้ไขได้ด้วยกลยุทธ์ต่าง ๆไม่แน่ใจว่ารูปแบบใดที่สั้นที่สุด แน่นอนว่าโปรแกรมของคุณจัดการในRuntimeErrorขณะที่ฉันเพิ่งขอให้ผู้ใช้เปลี่ยนเส้นทาง stderr ... ดังนั้นเราอาจจะอยู่ใน quibbles ; ^)
DLosc

1
อะไรคือสิ่งที่*_หา lambdas หรือไม่
mbomb007

4

Ghostscript

ยังไม่ได้เล่นกอล์ฟเนื่องจากฉันยังต้องใช้ฟังก์ชันการแยกวิเคราะห์

การใช้งานนี้ใช้_และ:แทนที่>และ/และจะต้องใช้อักขระโปรแกรมทั้งหมดเพื่อคั่นด้วยช่องว่าง นี่เป็นเพราะ>และ/ไม่ใช่ชื่อที่ถูกต้องใน Postscript และโอเปอเรเตอร์ไม่ใช่ตัวคั่นเอง แต่จะได้รับการแก้ไขเมื่อฉันเขียนโปรแกรมแยกวิเคราะห์

ส่วนแรกของรหัสควรมีความโปร่งใสพอสมควรเนื่องจากเป็นเพียงการทำซ้ำคำจำกัดความของฟังก์ชันของโอเปอเรเตอร์ !ความมหัศจรรย์ที่เกิดขึ้นในความหมายของ

/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

วิธี!การทำงานง่ายๆคือ: ครั้งแรกที่จะเพิ่มการโต้แย้งxไปfโดย prefixing xกับเนื้อหาของผลักดันมันกลับมาในกองและการตั้งชื่อสำเนาของผลที่ffun

จากนั้นจะตัดทั้งสแต็กขึ้นเป็นอาร์เรย์ .runandhideเป็นGhostscript extensionสำหรับการรันโค้ดแซนด์บ็อกซ์ซึ่งซ่อนเนื้อหาของอาเรย์ก่อนหน้าจากโพรซีเดอร์ที่ถูกเรียกใช้ dictคำสั่งผลักดันพจนานุกรมใหม่บนสแต็คกิงดิคที่ จำกัด ขอบเขตของชื่อที่กำหนดไว้ภายในจนendปรากฏว่ามันกลับออก นอกจากนี้ยังแทนที่=only(ตัวดำเนินการเอาท์พุทที่ฉันใช้@ด้วย) ด้วยตัวจำลองซึ่งจะไม่แสดงผลลัพธ์ในระหว่างการทดลองใช้งาน stoppedเป็น PostScript ที่เทียบเท่ากับtryคำสั่งที่พบในภาษาอื่นและมันจะคืนค่าจริงหากโพรซีเดอร์ของมันเกิดข้อผิดพลาดและเป็นเท็จถ้ามันรันจนเสร็จ

เมื่อการทดลองใช้งาน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 โดยexecในบรรทัดที่ 7

การรับฟังก์ชั่นหลายระดับที่เพิ่มขึ้นอ้างอิงและหนีออกมาอย่างถูกต้องเป็นอาการปวดหัวที่สุด ฉันสามารถประหยัดอีกเล็กน้อยเกี่ยวกับการดำเนินการ regex ถ้าฉันสามารถสันนิษฐานได้ว่าฟังก์ชั่นการซ้อนจะไม่ดำเนินการใด ๆ ที่ลึกกว่า 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 a k.pop()
mbomb007

@ mbomb007 ... ฉันคิดว่าสมองของฉันจะระเบิด (แต่ดูการแก้ไขล่าสุด - ฉันทำให้k.pop()สถานการณ์ซ้ำซากน้อยลงอีกแล้ว)
DLosc

คุณสามารถทำกลอุบาย exec / Translate สำหรับลูกแกะเหล่านั้นได้หรือไม่? ติดพวกเขาทั้งหมดในสายเดียว?
sirpercival

อีกความคิดเห็นหนึ่ง: ฉันสงสัยว่าคุณสามารถพึ่งพาฟังก์ชั่นการซ้อน <= 9 ด้วยภาษานี้
sirpercival

@sirpercival ใช่ฉันกำลังคิดที่จะลอง และไม่ฉันไม่คิด : ^ P
DLosc

1

Ceylon, 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, say yและ chain nใช้ตัวอักษรตัวสุดท้ายของชื่อสำหรับเวอร์ชันย่อเนื่องจากมีการชนน้อยกว่า (Trivia: ส้อมเดิมถูกกำหนดไว้อย่างนี้: [Data] fork(Data a, Data b, Data c) => a == blank then [b] else [c];- เมื่อฉันเปลี่ยนblankไปbนี้ยากจนเพราะตอนนี้เมื่อเทียบพารามิเตอร์aและbแทนaด้วยว่างเอาฉันเวลาในการแก้ปัญหา..)

zฟังก์ชั่นที่ใช้ร่วมกันเพราะ IDE ของฉันทำงานฟังก์ชั่นเหล่านั้น - เครื่องมือบรรทัดคำสั่งนอกจากนี้ยังสามารถเรียกใช้คนที่ไม่ได้ใช้ร่วมกัน


รุ่นลูปจริง ๆ แล้วจะโยน StackOverflowError ในบางจุดเสร็จแล้ว JVM ไม่มีการเพิ่มประสิทธิภาพสแต็กการเรียกซ้ำ (หรืออย่างน้อยก็ไม่มีซึ่งจะใช้งานได้สำหรับโปรแกรมของฉัน)
Paŭlo Ebermann
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.