Brain-Flak'ta Golf Yapmanın İpuçları


24

Brain-flak , benimle, DJMcMayhem ve 1000000000 arasında ortaklaşa yazılmış, yığın tabanlı bir turpit tarpit dilidir .

Bazı kullanıcılar Brain-Flak'ın gizemli şekillerinde çok deneyimlidir. Bu yüzden, bu soruyu bizim için ve umarım diğerleri için de bir yol olarak kurmanın ve bilgimizi toplulukla paylaşmanın ve bu dile “acı kullanmak için tasarlanmış” olan bu dile giriş çubuğunu düşürmenin iyi bir fikir olduğunu düşündüm. Ve belki de bize bazı yeni püf noktaları daha fazla tecrübe ile bize öğretin.

Peki beyin kaçağında golf oynamak için ne gibi ipuçlarınız var? Genel olarak golf problemlerini kodlamak için uygulanabilecek fikirlere bakıyorum (en azından beyin flakasına özgü) (örneğin, "yorumları kaldır" bir cevap değildir).

Lütfen cevap başına bir ipucu gönderin.

Yanıtlar:


22

Üçüncü Yığını Kullan

Başlığı okuduysanız, biraz kafanız karışmış olabilir. Kesinlikle Brain-Flak'ta sadece iki yığın var? Ancak sizi temin ederim ki Brain-Flak yazarken ve golf oynamanın en güçlü aracı olmasa da en güçlülerden biri.


"Üçüncü Yığın" nedir?

Her Brain-Flak programı üçüncü yığını bir şekilde ya da başka bir şekilde kullanır, ancak kullanımın çoğu sahnelerin ardında devam eder ve genellikle var olduğu gerçeğini göz ardı etmek genellikle yararlıdır. Programdaki her parantez, bir öğeyi yığına ekler veya kaldırır. Açık parantezlerin ([<üçü yığına bir öğe eklerken, üç konjugatının )]>tümü bir öğeyi yığından kaldırır. Yığındaki öğenin değeri, programın geçerli kapsamının değeridir ve nilantların kullanılması bu değeri belirli şekillerde değiştirir. Yakın parantez ), bir öğenin Üçüncü Yığından geçerli yığına taşınmasını sağlayan benzersiz bir işleve sahiptir; bir itme.

Umarım bu sizin için netleşiyor. Üçüncü Yığın, zaten yürütülmüş olan kodun dönüş değerlerini hatırlayan bir tür yığın. İki normal yığının ve Üçüncü Yığının izini süren basit bir program örneği üzerinden geçelim.

Örnek

Aşağıdaki programdan geçeceğiz. Bu program -3, 1, -2yığına iter .

(([()()()])(()))

Sıfırı üçüncü yığına iten üç açık ayraçla başlıyoruz.

Artık stoklarımız şu şekilde görünüyor: Üçüncü Yığın sağdaki ve aktif yığının da ^altında:

        0
        0
  0  0  0
  ^

(([()()()])(()))
   ^

Şimdi üç ()nilamız var. Bunlar normal iki yığına hiçbir şey yapmaz, ancak her biri Üçüncü Yığın üstüne bir tane ekler ve yığınlarımızın şöyle görünmesini sağlar:

        3
        0
  0  0  0
  ^

(([()()()])(()))
         ^

Şimdi ]yakın parantezlerin Üçüncü ]Yığındaki bir öğeyi çıkarmasından önce belirtildiği gibi karşımıza çıkıyor, ancak yığının üstünden çıkardığı öğeyi çıkarma işlevine sahip. Böylece yeni yığınlarımız şöyle görünecek:

       -3
  0  0  0
  ^

(([()()()])(()))
          ^

Bu mantıklı; [...]olumsuzlama yapar bu yüzden ]aşağı doğru çıkarmalı.

Şimdi bir tane yürütmeliyiz ). Büyük olasılıkla hatırlayacağınız )gibi, programda eşyaların yığına itildiği yer olduğundan, Üçüncü Yığın üstünü mevcut yığına taşıyacağız -3, ayrıca üçüncü yığındaki bir sonraki elemana ekleyeceğiz .

 -3  0 -3
  ^

(([()()()])(()))
           ^

Bir kez daha üç açık telimizden biriyle karşılaşıyoruz, böylece Üçüncü Yığımıza başka bir öğe daha ekleyeceğiz.

        0
 -3  0 -3
  ^

(([()()()])(()))
            ^

Daha önce ()de söylediğimiz gibi üçüncü yığımızın tepesini birer birer artıracağız.

        1
 -3  0 -3
  ^

(([()()()])(()))
              ^

Ve )Üçüncü Yığının üstünü aktif yığına taşıyacak ve aşağıya doğru ekleyeceğiz

  1
 -3  0 -2
  ^

(([()()()])(()))
               ^

Sonuncusu ), Üçüncü Yığını aktif yığına taşır ve Üçüncü Yığın üzerinde eklenmesi gereken herhangi bir öğe kalmadığından başka hiçbir şey yapmaz.

 -2
  1
 -3  0
  ^

(([()()()])(()))
                ^

Program sona erdi, bu yüzden sonlandırıyoruz ve çıktı veriyoruz.


Bu örnek, Üçüncü Yığın'ın ne olduğu ve yaptığı hakkında size bir fikir vermeyi amaçlamaktadır. Tüm işlemleri içermez, ancak umarım her birinin kendi başına ne yaptığını çözebilirsiniz. Eğer hala mücadele ediyorsanız, size yardımcı olmak için bu cevabın altına bir "cheatsheet" ekledim.

Tamam, peki ne?

Tamam, şimdi Üçüncü Yığını anlıyorsunuz, ama "Öyleyse ne?" “Üçüncü Yığın” olarak adlandırmasanız bile zaten kullanıyordunuz, Üçüncü Yığın açısından düşünmek golf oynamanıza nasıl yardımcı oluyor?

Bir probleme bakalım. Bir sayının üçgenini almak istiyorsun . Bu, n'den küçük olan tüm sayıların toplamıdır.

Bir yaklaşım, offstack'ta bir akümülatör oluşturmak ve siz geri sayım yaparken buna eklemek olabilir. Bu şuna benzeyen bir kod oluşturur:

(<>)<>{(({}[()])()<>{})<>}{}<>({}<>)

Çevrimiçi Deneyin!

Bu kod oldukça kompakt ve biri daha küçük olamayacağını düşünebilir. Bununla birlikte, üçüncü bir yığın bakış açısıyla yaklaşırsak, bunun son derece verimsiz olduğu anlaşılmaktadır. Akümülatörümüzü offstack'a koymak yerine, a ile üçüncü yığına koyabilir (ve kullandığımız sonunda geri alabiliriz ). Tüm sayıları bir kez daha tekrarlayacağız, ancak bu sefer Üçüncü Yığını arttırmak için fazla bir şey yapmak zorunda değiliz, program bizim için yapıyor. Bu gibi görünüyor:

({()({}[()])}{})

Çevrimiçi Deneyin

Bu kod daha önce yaptığımız oldukça iyi golf versiyonunun boyutunun yarısından daha az. Aslında bir bilgisayar araması, bu programın bu görevi gerçekleştirebilecek en kısa program olduğunu kanıtlamıştır. Bu program "tüm koşuların toplamı" yaklaşımı kullanılarak açıklanabilir, ancak Üçüncü Yığın yaklaşımı kullanılarak açıklandığında daha sezgisel ve açık olduğunu düşünüyorum.

Üçüncü Yığını ne zaman kullanırım?

İdeal olarak Brain-Flak'ta yeni bir problem üzerinde çalışmaya başladığınızda, kendinize Üçüncü Yığın'ı düşünerek bunu nasıl yapacağımı düşünmelisiniz. Bununla birlikte, ne zaman bir akümülatör tipini takip etmeniz ya da akan bir toplamınız olması gerektiğine dair genel bir kural olarak, bunu iki gerçek yığının yerine üçüncü yığınıza koymak iyi bir fikirdir.

Üçüncü yığınızı kullanmayı düşünmenin iyi bir fikir olabileceği bir başka zaman, diğer iki yığında bir miktar değer depolayacak boşluğunuz olmadığı zamandır. Bu, mevcut iki yığında manipülasyonlar yaparken ve nerede olduğunu takip etmek zorunda kalmadan daha sonra kullanmak üzere bir değer kaydetmek istediğinizde özellikle yararlı olabilir.

Üçüncü Yığın Sınırlamaları

Üçüncü Yığın birçok yönden çok güçlüdür ancak kendi sınırlamaları ve sakıncaları ile birlikte gelir.

İlk olarak, herhangi bir noktada Üçüncü Yığın için maksimum yığın yüksekliği derleme zamanında belirlenir. Bu, yığında bir miktar boşluk kullanmak istiyorsanız, programı yazarken bu alanı tahsis etmeniz gerektiği anlamına gelir.

İkincisi, Üçüncü Yığın Rasgele Erişim değildir. Bu, en yüksek değerden başka hiçbir değerde işlem yapamayacağınız anlamına gelir. Ek olarak, istif üzerindeki değerleri taşıyamazsınız (ilk iki elemanı değiştir).

Sonuç

Üçüncü Yığın güçlü bir araçtır ve her Brain-Flak kullanıcısı için gerekli olduğunu düşünüyorum. Brain-Flak'ta programlama hakkındaki düşüncelerinize alışmak biraz değişiyor, ancak doğru kullanıldığı zaman golf oynamaya gelince iyi ve inanılmaz bir fark yaratıyor.

Kopya kağıdı

İşte operasyonların listesi ve Üçüncü Yığını nasıl etkilediklerini

 Operation  |                 Action
====================================================
   (,[,<    | Put a zero on top of the Third Stack
----------------------------------------------------
     )      | Add the top of the Third Stack to the
            | second element and move it to the 
            | active stack
----------------------------------------------------
     ]      | Subtract the top of the Third Stack
            | from the second element and pop it
----------------------------------------------------
     >      | Pop the top of the Third Stack
----------------------------------------------------
     ()     | Add one to the top of the Third Stack
----------------------------------------------------
     {}     | Pop the top of the active stack and
            | add it to the top of the Third Stack
----------------------------------------------------
     []     | Add the stack height to the Third
            | Stack
----------------------------------------------------
   <>,{,}   | Nothing

1
Vay, fantastik bahşiş! Aslında bunu gördüğümde benzer bir cevap yazmaya gelmiştim. 1! Bunu Akümülatör olarak düşünmeyi tercih ediyorum , ancak Üçüncü Yığın metaforu gerçekten ilginç.
DJMcMayhem

Her zaman "çalışma alanı" veya "çalışma alanı" olarak adlandırdım.
sagiksp

Brainflak'ın TIO sürümünde {...}tüm yinelemelerin toplamını döndürür.
CalculatorFeline

@CalculatorFeline Evet, bu Brain-Flak'ın hemen hemen tüm sürümleri için çok erken olanlar hariç. Ancak bu yazıya özellikle neden yorum yaptığınızdan emin değilim.
Buğday Sihirbazı,

<>,{,} | Nothing
CalculatorFeline

21

Modül / kalanı bulma

N modulo m bulunması birçok zorluk için önemli olan temel aritmetik işlemlerden biridir. Durumlar için > 0 m ve n> = 0 , aşağıdaki 46-bayt pasajı kullanılabilmektedir. N'nin bir sonraki m ile aktif yığının tepesinde olduğu varsayılır ve bunları n mod m ile değiştirir . İstiflerin geri kalanını bozulmadan bırakır.

({}(<>))<>{(({})){({}[()])<>}{}}{}<>([{}()]{})

Bu açıklamalı sürüm, programın bazı noktalarındaki yığın içeriğini gösterir. ;iki yığını ayırır ve .etkin yığını işaretler.

. n m;
({}(<>))<>
{   . m; r 0   (r = n - km)
    (({}))
    . m m; r 0
    {({}[()])<>}
    {}
}
m-n%m-1 m; . 0
{}<>([{}()]{})
. n%m;

Bana Açıklama içermeyen kısım (yaptıklarını anlamak için biraz zaman aldı {({}[()])<>}), ama ben ... Genius :-) anladım bir kez
ETHproductions

11

Push-Pop Artıklığı

Bu büyük bir tane. Aynı zamanda biraz nüanslı bir tane.

Fikir şu ki, eğer bir şeye basar ve sonra hiçbir şey yapmadan çıkarırsanız, onu itmemelisiniz.

Örneğin, bir şeyi offstack'a taşımak ve ardından bir tane eklemek istiyorsanız, aşağıdaki kodu yazabilirsiniz:

({}<>)({}())

Bu böyle daha basit olabilir:

({}<>())

İlk program, öğeyi hareket ettirir, tekrar alır ve bir tane ekler, ikincisi de ikisini birden basar.

Bu örnek basitti, ancak çok daha karmaşık hale gelebildi. Mesela:

({}<({}<>)><>)(<((()()()){}[((){}{})])>)

Buradaki azalma daha az açıktır ancak programdaki 4. pop, 2. basışla azaltılabilir, şöyle:

(<((()()()){}[((){}<({}<>)><>{})])>)

Golf repertuarımdaki en güçlü araç bu, ancak bu konuda başarılı olmak için biraz pratik gerektiriyor. Bunları bir süredir yaptıktan sonra, neredeyse anında bunları tespit edebileceksiniz.


İkinci örnekte, ilk parantez çiftindeki kısım eşdeğer değil ({}<{}>)mi?
feersum

@ feersum Hayır değil. ({}<{}>)Öğeyi tamamen yok ederken, yığındaki ikinci öğenin kopyasını kapalı yığına taşır . Bununla birlikte, bu programlar burada sadece işyerinde prensibi vurgulamak için en uygun yol değildi.
Buğday Sihirbazı 4

6

Tam sayılarınızı optimize edin

Tamsayılar Brain-Flak'ta temsil etmek için can sıkıcıdır. Neyse ki Golf a Brain-Flak Integer 'ın size yardımcı olacağı bir sorumuz var . (Sorunun tam sayıyı yığına itmek için tasarlandığını unutmayın, bu nedenle push-pop artıklığı muhtemelen daha gerçekçi programlar için geçerlidir.)


Ayrıca, kolaylık sağlamak için çevrimiçi olarak bu algoritmalardan birini çalıştıran brain-flak.github.io/integer/ seçeneğine de sahip olduğumuzu unutmayın .
DJMcMayhem

@DrMcMoylex Beyin-Flak'taki tamsayı metagolfer uygulamasında olduğu gibi şimdi ihtiyacımız olan her şey!
Neil


5

Ekstra döngü sayaçlarına bas

Sık sık, gibi bir şey yapmak isteyeceksiniz

Yığındaki her elemanda X işlemi yapın

veya

Yığındaki her bir bitişik eleman çiftinde X işlemini gerçekleştirin

Girdi '0'lar içerebiliyorsa veya X işleminin sonucu 0 veriyorsa, bu gerçekten sakıncalıdır. Çünkü yapmanız gerekenler:

([])
{

  {}

  ({}...<>)
  ([])

}

Yapmak için X sonradan ardından her elemana ve

<>
([])
{
  {}
  ({}<>)
  <>
  ([])
}
<>

Diziyi tekrar ters çevirmek için.

Biz komşu elemanların çiftleri üzerinde operatiing eğer Daha da kötüsü, biz yapmanız gerekir ([][()])yerine ([]). Bu gerçekten sakıncalıdır. İşte püf nokta: Her bir öğeye X yaparken , hemen üzerindeki alternatif yığının üzerine bir 1 itin X(element). Sonra, tersine çevirirken, basitçe

<>
{
  {}
  ({}<>)
  <>
}
<>

Bu 8 bayt daha kısadır, bu yüzden 1'i itmek için fazladan kodda bulunduğunuzda, 4-6 bayttan tasarruf edersiniz. Daha somut bir örnek için, bir dizinin delta alma görevini karşılaştırın. Bu numara olmadan ihtiyacın olacak:

([][()]){

    {}

    ([{}]({})<>)<>

    ([][()])

}{}{}<>

([])
{{}({}<>)<>([])}<>

62 için. Bu numara ile, sahip olacaksınız.

([][()]){

    {}

    ([{}]({})<>)(())<>

    ([][()])

}{}{}<>

{{}({}<>)<>}<>

58 için. Doğru şekilde kullanılırsa (örneğin, ilk önce tersine çevirme ve iki ([][()])tanesini sonradan kaldırmak ), bu, özel senaryolarda daha da fazla tasarruf sağlayabilir.


3

'Yığın Yüksekliği' niladından yararlanın

Özellikle zorluklarında veya girdilerin boyutunu her zaman bildiğiniz zorluklarda, []tamsayılar oluşturmak için 'Yığın Yüksekliği' niladından yararlanabilirsiniz.

Bunun üzerine varsayımsal bir meydan okuma ile çalışalım: çıktı CAT. Golf yapmayan yol, çevrimiçi tamsayı golfçüyü 67, 65 ve 84'ü itmek için kullanmaktır.

(((((()()()()){}){}){}()){}())

(((((()()()()){}){}){}){}())

((((((()()()){}()){}){})){}{})

(Netlik için yeni satırlar). Bu 88 bayttır ve o kadar da büyük değil. Bunun yerine değerler arasındaki ardışık farkları zorlarsak, çok fazla tasarruf sağlayabiliriz. Bu yüzden ilk numarayı bir push çağrısına sardık ve 2'yi çıkardık:

(   (((((()()()()){}){}){}()){}())  [()()] )

Ardından bu kodu alıyoruz, bir push çağrısına sardık ve sonuna 19 ekledik:

(  ((((((()()()()){}){}){}()){}())[()()])   ((((()()()){})){}{}()) )

Bu 62 bayt, bir kuyruklu 26 bayt golf için!

Şimdi burada yığın yüksekliği niladından faydalanabileceğimiz yer burasıdır. Biz 19 iterek başlamak zaman, biz böylece yığın 2 ürün, zaten orada olduğunu bilmek []için değerlendirecektir 2. Bunu daha az sayıda baytta 19 oluşturmak için kullanabiliriz. Açık olan, içini ()()()değiştirmektir ()[]. Bu olsa sadece iki bayt kaydeder. Biraz daha titremeyle, 19 ile itebileceğimiz anlaşıldı.

((([][]){})[]{})

Bu bize 6 bayt kazandırır. Şimdi 56'ya düştük.

Bu ipucunun bu cevaplarda çok etkili bir şekilde kullanıldığını görebilirsiniz:


Kişisel CATprogram aslında iter TAC: P
Buğday Sihirbazı


2
Bunun bir ipucu olduğunu biliyorum ama kendime 50 bayt yardım edemiyorum .
Buğday Sihirbazı 19

1
Kötüye kullanıma yardımcı olmak için garip fakat bazen etkili olan bir ipucu []: kodunuzu 0s ile (<>)önceden hazırlamak. Bu sadece, yine de kodu tersine doğru itmeyi planlıyorsanız çalışır, ancak doğru numaraya rastlarsanız kodunuzu biraz azaltabilirsiniz. 6 saniyeyi eklediğim ve kullandığım kadar s kullanmamı sağlayan oldukça uç bir örnek0[]()
Jo King

2

Wiki kullanın

Bir vikimiz var ! Bazı kısa geliyor, ancak yararlı bir kaynaktır. Kodunuza yapıştırabileceğiniz yararlı, iyi golfe sahip, yığınlanmış temiz programların bir listesi vardır. Bir şeyi yapmak istiyorsanız, birisinin daha önce yapmış olabileceğini düşündüğünüz bir şey varsa, bunun wiki'de olması.


2

Daha iyi döngü

İşte kolay bir tane:

Oldukça yaygın bir yapı:

(({})<{({}[()]<...>)}{}>)

N kere döngü yapmak istediğiniz yerde ama n'yi koruyun. Ancak bu şöyle yazılabilir:

({<({}[()]<...>)>()}{})

2 bayt kaydetmek için.

Oldukça yaygın bir başka paradigma da

([])({<{}>...<([])>}{})

Hangi tüm döngü döngü ve biriktirecek. Bazı süslü matematik nedeniyle bu aynıdır:

(([]){[{}]...([])}{})

1

Negatiflerini kontrol et

Bazen stratejik olarak [...]monad ile neyin olumsuzlanacağını seçerek birkaç byte golf oynayabilirsiniz .

Basit bir örnek iç içe geçmiş [...]s'dedir. Örneğin [()()()[()()]]sadece olabilir[()()()]()()

Bir değerin başlangıç ​​parantezlerinden herhangi biri olup olmadığını kontrol etmek istediğinizi söyleyin (<{[. İlk girişim, her bir karakter arasındaki farkı çıkarmak ve onu çıkartmaktan kaçınmaktır.

({}<(((((       #Push the differences between start bracket characters
(((()()()()){}){}){})   #Push 32
[()])                   #Push 31
[((()()())()){}{}])     #Push 20
){})><>)                #Push 40
<>({<(([{}]<>{}))>(){[()](<{}>)}<>})

Ancak, bunun yerine farkların negatif versiyonlarını iterek 4 byte tasarruf edebilirsiniz!

({}<(((((       #Push the differences between start bracket characters
((([()()()()]){}){}){}) #Push -32
())                     #Push -31
((()()())()){}{})       #Push -20
){})><>)                #Push -40
<>({<(({}<>{}))>(){[()](<{}>)}<>})

Genel olarak, bu sizi çok fazla kurtarmaz, ancak etrafındakileri değiştirmek için çok az çaba [...]harcar. Daha sonra düşürmek yerine, birden fazla kez artırırken tasarruf etmek için pozitif yerine bir sayacın negatifini zorlayabileceğiniz durumlara dikkat edin. Ya dışarı takas (a[b])ile ([a]b)iki sayı negatif yerine pozitif arasındaki fark yaratmak için.


1
Benzer şeyler sıfır <a<b>c>-> <abc>ve <a[b]c>-> ile yapılabilir <abc>.
Buğday Sihirbazı
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.