Vardiya Tercümanı Yaz


10

EDIT: Bazılarınızın şüphelendiği gibi, resmi yorumlayıcıda bir hata vardı: kompozisyonun sırası .tersine çevrildi. Tercümenin iki versiyonu vardı ve burada yanlış olanı kullandım. Örnekler de bu yanlış versiyon için yazılmıştır. Yorumlayıcıyı depoda ve aşağıdaki örneklerde düzelttim. Açıklaması >da biraz belirsizdi, bu yüzden bunu düzelttim. Ayrıca, bu kadar uzun sürdüğü için özür dilerim, gerçek hayattaki bazı şeylere takıldım.

EDIT2: Yorumcumun uygulamada .örneklere yansıyan bir hatası vardı (tanımlanmamış davranışa dayanıyorlardı). Sorun düzeltildi.

Giriş

Shift , birkaç yıl önce yaptığım ama bugün yayınladığım ezoterik bir işlevsel programlama dilidir. Yığın tabanlıdır, ancak Haskell gibi otomatik köri vardır.

Şartname

Shift'te iki veri türü vardır:

  • Keyfi pozitif bir arititeye (giriş sayısı) sahip olan ve bir çıkış listesi döndüren işlevler . Örneğin, tek girişini çoğaltan bir fonksiyonun 1 değeri ve iki girişini değiştiren bir fonksiyonun 2 değeri vardır.
  • Hepsi özdeş olan ve fonksiyon olmamaktan başka amacı olmayan boşluklar.

Bir Shift programı , her biri tek bir ASCII karakteri olan sıfır veya daha fazla komuttan oluşur . Toplamda 8 komut vardır:

  • !( Uygulanır ) bir işlevi çıkar fve bir değer xistiften ve geçerli fiçin x. Eğer fArity 1 vardır, liste f(x)istifin önünde eklenir. Arity varsa, yığına n > 1yeni bir (n-1)işlev eklenir g. Girdileri alır ve geri döner .x1,x2,...,xn-1f(x,x1,x2,...,xn-1)
  • ?( boş ), bir boş yığını yığına iter.
  • +( klon ) yığına girdisini çoğaltan tekli bir işlevi iter: herhangi bir değer xeşlenir [x,x].
  • >( Kaydırma ) yığına bir alır bir tekli fonksiyonu iter n-ary fonksiyonu fve bir döner (n+1)-ary fonksiyonu gilk argüman yok sayar x, çağrı fgeri kalanlar üzerinde, ve çiviler xsonucu önünde. Örneğin, shift(clone)girdileri alan a,bve döndüren bir ikili işlevdir [a,b,b].
  • /( Çatal ) yığına, üç giriş alır üçlü fonksiyonu iter a,b,cve döner [b]ise aboş olduğu ve [c]başka türlü.
  • $( call ) yığına bir işlevi fve değeri açan xve tam olarak olduğu gibi uygulanan bir ikili işlevi iter .fx!
  • .( Zincir ) yığına çıkar iki işlevi bir ikili işlev iter fve gbir işlev: ve bunların bileşimi verir haynı Arity sahiptir f, ve normal olarak, girişlerine uygulandığı fonlara ve daha sonra tam olarak geçerlidir g(çağrı sonucu onun çağının dikte ettiği kadar), fsonuçta kalan çıktıdan kullanılmayan öğelerle h. Örneğin, varsayalım fikinci argüman klonunu ve bir ikili işlevdir golan çağrı . Yığın içeriyorsa [f,g,a,b,c]ve .!!içeriyorsa [chain(f,g),a,b,c]; !!sonra yaparsak , o fzaman ilk önce a,b,[a,b,b], daha sonra gilk 2 elementine uygulanır, çünkü arity 2 olduğundan üretilir [a(b),b]ve yığın sonunda olur [a(b),b,c].
  • @( diyelim ) girdisini döndüren tekli bir işlevi iter ve 0boşsa ve bir işlevse yazdırır 1.

!Yığına bir değer basmak dışında tüm komutların girdi yapmanın bir yolu olmadığını ve herhangi bir çıktıyı almanın tek yolunun kullanmak olduğunu unutmayın @. Bir program, komutları tek tek değerlendirerek, "say" çağrıldığında 0s veya 1s yazdırarak ve çıkarak yorumlanır. Burada açıklanmayan herhangi bir davranış (bir boşluk uygulamak, 0 veya 1 uzunluğunda bir yığın uygulamak, bir boşluk üzerine "zincir" çağırmak vb.) Tanımlanmamıştır: yorumlayıcı çökebilir, sessizce başarısız olabilir, girdi isteyebilir veya her neyse.

Görev

Göreviniz Shift için bir tercüman yazmaktır. Yorumlanması için bir Shift programı olan STDIN, komut satırı veya işlev bağımsız değişkeninden almalı ve STDOUT'a yazdırmalı veya sonuç olarak 0s ve 1s'nin (muhtemelen sonsuz) çıktısını döndürmelidir . Bir işlev yazarsanız , sonsuz uzunlukta çıkışlara bir şekilde erişebilmeniz gerekir (Python'daki jeneratör, Haskell'deki tembel liste, vb.). Alternatif olarak, başka bir girdi, bir sayı alabilir nve ndaha uzunsa çıktıdaki en az karakterleri döndürebilirsiniz n.

En düşük bayt sayısı kazanır ve standart boşluklara izin verilmez.

Test Durumları

Bu Shift programı yazdırır 01:

?@!@@!

Soldan başlayarak: itme boş, itme diyelim , o zaman uygulamak söz taslağa. Bu çıktı 0. Ardından, itme demek iki kez, ikinci uygulamak söz ilkine. Bu çıktı 1.

Bu program sonsuza kadar dönerek çıktı üretmez:

$+.!!+!!

İtme çağrı ve klon , daha sonra uygulamak zincirini (biz iki ihtiyaç onlara !beri s zincir ikili işlevdir). Artık yığın, bir bağımsız değişkeni alan, çoğaltan ve ikinci kopyayı ilk kopyayı çağıran bir işlev içeriyor. İle +!!, bu işlevi çoğaltır ve kendi başına çağırırız.

Bu program yazdırır 0010:

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

Bir alanı itin ve söyleyin . Ardından, ikinci argümanını bkopyalayan, sonra ilkini kopyalayan ave onu kendisiyle birleştiren bir ikili işlev oluşturun, ardından kompozisyonu bdöndürerek kopyasına uygular [a(a(b)),b]. O uygula demek ve boş, ardından uygulamak söz yığını üzerinde kalan iki elemanlarına.

Bu program yazdırılır 0. !!!Eklediğiniz her biri için ek yazdırır 0.

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

Bir alanı itin ve söyleyin . Ardından, f,g,xgirdi olarak ve geri dönen üçlü bir işlev oluşturun [f,f,g,g(x)]. Bu işlevi klonlayın ve kendinize, örneğin boşluğa uygulayın. Bu uygulama yığını değiştirmez, bu nedenle fonksiyonu istediğimiz kadar tekrar uygulayabiliriz.

Bu program sonsuz sekansı yazdırır 001011011101111..., burada 1s sayısı her zaman bir artar:

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

Depo açıklamalı bir sürüm içeriyor.


Burada biraz kafam karıştı. Shift komutunda olduğu gibi "alır" yazdığınızda, pop'ları mı yoksa uygula komutu tarafından mı uygulandınız?
tecywiz121

1
Ayrıca, zincirinizden nasıl çalışacağı konusunda tam olarak emin değilim. Lütfen bir örnekle açıklığa kavuşturabilir misiniz?
tecywiz121

@ tecywiz121 Bunu şu şekilde anlıyorum: yığının üstünde iki fonksiyonun olduğunu söyle, f(x1, x2, ..., xn)ve g(y1, y2, ..., ym). Arayan .bir fonksiyonu bunlardan ve iter hem pops h(z1, z2, ..., zn). Şimdi tüm bu argümanları yavaş yavaş körükleyerek yiyebilirsiniz !. Bu ntür uygulamalardan sonra , kalan işlevin yalnızca bir bağımsız değişkeni vardı ve bu noktada bazı yeni değerleri iten ve hemen yığındaki değerleri tüketen ve bunlara çağrı yapan hesaplar f(z1, z2, ..., zn)(yani f, curried tüm bağımsız değişkenlere uygulanır) . mg
Martin Ender

@ MartinBüttner Zgarb kurallara uygun olduğunu düşünürse, çıktının maksimum boyutunu tanımlayan ikinci bir girdi parametresi kullanabilirsiniz. Bu tembel değerlendirme sorunu için de bir çözüm olacaktır.
randomra

@ tecywiz121 .tam olarak Martin'in tanımladığı gibi çalışır, ancak değerlerden fdaha az bir liste döndürürse m, sonuç tanımsızdır (kompozisyon arity'ye sahiptir n, bu nedenle yığından daha fazla argüman yiyemez). Esas olarak, çıktısı, süreleri kullanılarak itilen ve uygulanan fgeçici bir yığın olarak kullanılır ve bunun sonucu, ana yığına eklenir. gm!
Zgarb

Yanıtlar:


12

Python 2, 752 667 534 506 445 436 427 404 398 393 bayt

Bu hiçbir şekilde kısa değil ... ama elimden geleni yaptım. Herhangi bir golf önerileri çok takdir edilecektir ...

EDIT6: Bu artık bir işlev yerine bir betik. Bir dosyaya (shift.py, forex) kaydedin, ardından ile çalıştırın $ python shift.py '<my_input>'. Girişi tek tırnak içine aldığınızdan emin olun yoksa bash giriş karakterleri ile delirir.

EDIT7: Aaaaaave ... artık okunamıyor. Ama 23 bayttan daha kurtuldum, bu iyi, sanırım? Ben de ungolfed versiyonunu göndereceğim

EDIT8: @Zgarb sayesinde bir golf daha.

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

EDIT: golf yardım için @DLosc sayesinde! 85 bayt azaltmayı başardı.

EDIT2: bir ton gereksiz sarmalayıcıyı kesin ve 133 bayt daha düşürdü!

EDIT3: ... ve 28 daha sohbet sayesinde @ Sp3000 ve @orlp sayesinde!

EDIT4: @orlp & @ Sp3000'in yardımıyla tüm dekoratörleri kaldırdı ve şimdi 61 bayt daha kısa.

EDIT5: meeeeee yardım, bu golf duramazsın .... 9 bayt daha gitti. Son baskı deyiminden kurtulmak başka bir 7 tasarruf sağlar, ancak daha sonra bir döngüde m () çalıştırırsanız, tüm çıktı aynı satırda ... Tamam mı?

İşte ungolfed versiyonu:

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

Temel fikir, python listesinin bir yığın olarak çok iyi çalıştığı ve depolayarak u=k.append, sadece karakterleri kaydetmiyorum, aynı zamanda @uişlevleri itmek için bir dekoratör olarak da kullanabilirim (artık değil!).

N-arity işlevlerine etki eden işlevlerin çiftinin isteğe bağlı sayıda argümanı kabul etmesi *argsgerektiğinden, f.func_code.co_argcount izleme ilk planımın bir değişkenle değiştirilmesi gerektiği anlamına geliyordu. dekoratör özniteliği.

Sonsuz programlarla ilgili olarak, yorumlayıcı maksimum özyinelemeli derinliğe ulaşana kadar çalışır; alttaki RuntimeError işleyicisi bu noktada sessizce çıkmasını sağlar ve o zaman geçerli çıktı dizesini yazdırır.

Test senaryoları:

>>> 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
İlk tepkim: @ _ @ Cidden, yine de, iyi iş - yığına gerçek işlevler koymak gerçekten düzgün bir çözüm. Birkaç ipucu: 1) Üçlü operatörler genellikle şu veya bu şekilde kısaltılabilir . 2) yerini alabilir ['1','0'][...]sadece birlikte '10'[...]. 3) Neden x is 0ve değil x==0(veya x<1)? 4) belirten rahatsız etmeyin RuntimeError, sadece exceptyapacağız. 5) Python 2 kullandığınız için, sekmeler ve boşluklar farklı girinti seviyeleri olarak sayılır - çirkin, ancak sizi ~ 25 bayt kurtarmalıdır.
DLosc

1
Bunu kesebilmelisiniz x.a==1and x(y)or u(a(x.a-1)(b.partial(x,y)))- mantıksal işleçler hala kısa devre yapıyor, ancak üçlüden daha az karakter kullanın. Sonra kullanarak başka byte tasarrufu x.a-1(eğer 0 / yanlış koşulu olarak xaksi / sıfırdan farklı, gerçek 1'dir) ve else ifadeleri ardından 'takas ve: x.a-1and u(a(x.a-1)(b.partial(x,y)))or x(y). (Beni geçtiğin için biraz daha golf yapmam lazım ...; ^))
DLosc

1
Benimkiyle benzer bir problemle karşılaştıktan sonra, neyin başarısız olduğunu anlıyorum - eğer x.a==1doğruysa ama bir x(y)şey döndürürse, u(...)aynı zamanda değerlendirmeye çalışır . Ama görünüşe göre size verebilecek olan 3 bayt tasarruf etmenize gerek yok! Kabul ediyorum efendim: beni aştın.
DLosc

1
Sadece bir kelime oyunu: belirtilen çıktı biçiminde boşluk yok - hangisinin en kısa olduğundan emin değilseniz çeşitli stratejilerle çözebilirsiniz . Tabii ki, programınız RuntimeErrorbenimkinden sadece stderr'i yönlendirmesini ister ... bu yüzden muhtemelen tartışmalardayız. ; ^)
DLosc

1
Nedir *_lambdas içinde için?
mbomb007

4

Ghostscript

Henüz golf değil, çünkü hala ayrıştırma işlevini çalışmak gerekir.

Bu uygulama _ve :yerine >ve kullanır ve /tüm program karakterlerinin boşluklarla ayrılmasını gerektirir. Bunun nedeni >ve /Postscript geçerli isimler değildir ve operatörler kendi kendini sınırlayan değil, ancak ben ayrıştırıcı yazarken bu düzeltilecektir.

Kodun ilk kısmı, yalnızca işleç işlevlerinin tanımlarını tekrarladığı için oldukça şeffaf olmalıdır. Büyü, tanımında olur !.

/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

Yolu !çalışmaları basittir: Öncelikle, argüman ekler xiçin fönüne işaret xiçeriğine fgeri yığın iterek ve sonucu bir kopyasını adlandırma fun.

Daha sonra tüm yığını bir dizi olarak sarar. korumalı kod çalıştırmak için, önceki dizinin içeriğini çağrıldığı yordamdan gizleyen .runandhidebir Ghostscript uzantısıdır . dictKomut kadar içinde tanımlanmış adlar kapsamını daraltarak, dict yığın Yeni sözlük iter endonu geri çekil pops. Ayrıca =only(kullandığım çıkış operatörü @) deneme çalışması sırasında çıktıyı bastırarak kukla bir tanesiyle değiştirir. diğer dillerde bulunan ifadenin stoppedPostScript eşdeğeridir tryve yordamı bir hata verdiğinde true değerini, tamamlanmaya çalıştığında false değerini döndürür.

Deneme çalıştırması funtamamlandığında, program orijinal yığını gizli diziden geri yükler ve funhatasız olarak tamamlanırsa, çıktıyı koruyarak gerçek olarak çalıştırır.


2

Python3, 685 670 634 633 bayt

Eminim ki bu şimdiye kadar golf oynadığım en uzun şey. Eskiden biraz okunabilirdi, ama @ sirpercival'ın tavsiyelerine uymak bu dezavantajı ortadan kaldırdı !

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)

kgibi dizileri olarak gösterilmiştir işlevleri içeren yığın, bir "h(0,0)"(olup c h ain ). Bir işlev başka bir işleve argüman olarak iletilen zaman, alır reprd' ve tüm numaralar artırılır: "h('h(1,1)',0)". Tüm 0s bir işlevde değiştirildikten sonra, her şey geçirilir eval, böylece uygun Python işlevi çağrılır - bunların çoğu, satır 6'daki büyük dizeden exec7 satırında üretilen lambda işlevleridir .

İç içe işlevlerin birden fazla düzeyini artırmak, tırnak içine almak ve düzgün şekilde kaçmak en büyük baş ağrısıydı. İşlev iç içe yerleştirmenin 9 düzeyden daha derine ilerlemeyeceğini varsayabiliyorsam, regex işlemlerinde biraz daha fazla tasarruf edebilirim, ancak yorumlarda belirtildiği gibi, muhtemelen güvenli bir varsayım değildir.

Kodun önceki sürümü kaldırıldı:

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

Bu uygulamanın olası bir kusuru, özyineleme kullanmasıdır, bu nedenle sonsuz olması gereken programlar maksimum özyineleme derinliğini çok hızlı bir şekilde vurur. (Sonsuz bir program çalıştırdığınızda muhtemelen stderr'i yeniden yönlendirmek istersiniz - aksi takdirde yığın izlemesi gerçek çıktıyı değiştirir.) Bunun dışında her şey çalışıyor gibi görünüyor.


Yukarıdaki programı oluşturan ve daha sonra çalıştıran bir program yazabilir misiniz? lambda aVe gibi sıkıştırılabilir olması gereken çok sayıda yinelenen kodunuz var k.pop().
mbomb007

@ mbomb007 ... Sanırım beynim patlayacaktı. (Ama son düzenlemeye bakın - k.pop()Durumu biraz daha az tekrarlı
yaptım

Tüm bu lambdalar için exec / translate hile yapabilir misin? hepsini tek bir dizeye yapıştırmak?
sirpercival

başka bir yorum: Bu dil ile <= 9 fonksiyon yuvalama güvenebilirsiniz şüpheliyim
sirpercival

@sirpercival Evet, denemeyi düşünüyordum. Ve hayır, sanmıyorum. : ^ P
DLosc

1

Seylan, 1167 1057 1031

Tek tip python versiyonları kadar kısa anlamıyorum ...

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);}

İşte aynı kodun biçimlendirilmiş (ve yorumlanmış) bir sürümü (boşluklar / yeni satırlar / yorumlar ile 4867 bayt olur):

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);
}

Klonlama e, kaydırma t, çatal k, çağrı l, söyle yve zincir işlevleri n, kısaltılmış sürüm için adların son harfini kullanır, çünkü bu daha az çarpışma verdi. (Bilgiler: çatal aslen bu şekilde tanımlandı: [Data] fork(Data a, Data b, Data c) => a == blank then [b] else [c];- Ben değiştirildi zaman blankiçin b, bu kırdı, şimdi parametreleri karşılaştırıldığında çünkü ave bbunun yerine aboş ile bana ayıklama için biraz zaman aldı..)

zFonksiyon benim IDE bu işlevleri çalıştığından, paylaşılır - komut satırı aracı ayrıca sigara paylaşılanları çalıştırabilirsiniz.


Döngü sürümleri aslında bir noktada bir StackOverflowError atar ve sonra bitirir. JVM'de özyineleme yığını optimizasyonu yoktur (veya en azından programım için işe yarayacak hiçbiri yoktur).
Paŭlo Ebermann
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.