Brainfuck Hello World aslında nasıl çalışıyor?


118

Biri bunu bana gönderdi ve Brainfuck'ta bir merhaba dünya olduğunu iddia etti (ve umarım öyledir ...)

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.

Bir işaretçiyi hareket ettirerek ve öğeleri artırıp azaltarak çalışmasının temellerini biliyorum ...

Yine de, aslında nasıl çalıştığını hala bilmek istiyorum. İlk etapta ekranda herhangi bir şeyi nasıl yazdırır? Metni nasıl kodlar? Hiç anlamıyorum ...


31
Bu dilde yazılmış bir uygulamayı sürdürmek oldukça zor olmalı ..
e2-e4

18
@ ring0: nah, bu salt yazılabilir bir dil.
LetMeSOThat4U

pratik kullanım nedir?
Yash Kumar Verma

10
@YashVerma buna gerek yok ..
Insane

49
@YashVerma Dil adına açıkça belirtilmiştir.
Mateen Ulhaq

Yanıtlar:


255

1. Temel Bilgiler

Brainfuck'ı anlamak için, 0her biri tarafından başlatılan sonsuz hücre dizisi hayal etmelisiniz .

...[0][0][0][0][0]...

Brainfuck programı başladığında herhangi bir hücreyi işaret eder.

...[0][0][*0*][0][0]...

İşaretçiyi sağa >hareket ettirirseniz, işaretçiyi X hücresinden X + 1 hücresine hareket ettirirsiniz.

...[0][0][0][*0*][0]...

Hücre değerini artırırsanız +şunları elde edersiniz:

...[0][0][0][*1*][0]...

Hücre değerini tekrar artırırsanız, +şunları elde edersiniz:

...[0][0][0][*2*][0]...

Hücre değerini düşürürseniz -şunları elde edersiniz:

...[0][0][0][*1*][0]...

İşaretçiyi sola <hareket ettirirseniz, işaretçiyi X hücresinden X-1 hücresine taşırsınız

...[0][0][*0*][1][0]...

2. Giriş

Karakteri okumak için virgül kullanırsınız ,. Yaptığı şey şudur: Standart girdiden karakteri okuyun ve ondalık ASCII kodunu gerçek hücreye yazın.

ASCII tablosuna bir göz atın . Örneğin, ondalık kodu !is 33, while ais 97.

Peki, BF program belleğinizin şöyle göründüğünü hayal edelim:

...[0][0][*0*][0][0]...

Standart girdinin yerini varsayarsak a, virgül ,operatörü kullanırsanız , BF'nin yaptığı şey, aondalık ASCII kodunu 97belleğe okumaktır :

...[0][0][*97*][0][0]...

Genelde böyle düşünmek istersiniz, ancak gerçek biraz daha karmaşıktır. Gerçek şu ki, BF bir karakter değil, bir bayt (o bayt ne olursa olsun) okur. Size örnek göstereyim:

Linux'ta

$ printf ł

baskılar:

ł

belirli bir cila karakteri. Bu karakter ASCII kodlamasıyla kodlanmamıştır. Bu durumda, UTF-8 kodlamasıdır, bu nedenle bilgisayar belleğinde birden fazla bayt alırdı. Onaltılık bir döküm yaparak bunu kanıtlayabiliriz:

$ printf ł | hd

hangi gösterir:

00000000  c5 82                                             |..|

Sıfırlar ofsettir. 82birinci ve c5temsil eden ikinci bayttır ł(sırayla onları okuyacağız). |..|bu durumda mümkün olmayan grafik gösterimdir.

Peki, łtek bayt okuyan BF programınıza girdi olarak geçerseniz , program belleği şöyle görünecektir:

...[0][0][*197*][0][0]...

Neden 197? Eh 197ondalık sayıdır c5onaltılık. Tanıdık geliyor mu? Elbette. İlk baytı ł!

3. Çıktı

Karakteri yazdırmak için nokta kullanırsınız .Ne yapar: Gerçek hücre değerini ondalık ASCII kodu gibi ele aldığımızı varsayarsak, karşılık gelen karakteri standart çıktıya yazdırın.

Peki, BF program belleğinizin şöyle göründüğünü hayal edelim:

...[0][0][*97*][0][0]...

Şimdi nokta (.) Operatörünü kullanırsanız, BF'nin yaptığı şey yazdırmaktır:

bir

Çünkü aASCII'deki ondalık kod 97.

Örneğin, bunun gibi BF programı (97 artı 2 nokta):

++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++ ..

Gösterdiği hücrenin değerini 97'ye çıkaracak ve 2 kez basacaktır.

aa

4. Döngüler

BF döngüde döngü başlangıcı [ve döngü bitiminden oluşur ]. Koşulun gerçek hücre değeri olduğu C / C ++ 'da olduğu gibi düşünebilirsiniz.

Aşağıdaki BF programına bir göz atın:

++[]

++ gerçek hücre değerini iki kez artırır:

...[0][0][*2*][0][0]...

Ve []sanki while(2) {}sonsuz döngüdür.

Diyelim ki bu döngünün sonsuz olmasını istemiyoruz. Örneğin şunları yapabiliriz:

++[-]

Dolayısıyla, bir döngü her döngüde gerçek hücre değerini azaltır. Gerçek hücre değeri 0döngü bittiğinde:

...[0][0][*2*][0][0]...        loop starts
...[0][0][*1*][0][0]...        after first iteration
...[0][0][*0*][0][0]...        after second iteration (loop ends)

Başka bir sonlu döngü örneğini ele alalım:

++[>]

Bu örnek, döngünün başladığı hücrede döngüyü bitirmemiz gerektiğini gösterir:

...[0][0][*2*][0][0]...        loop starts
...[0][0][2][*0*][0]...        after first iteration (loop ends)

Ancak başladığımız yerde bitirmek iyi bir uygulamadır. Neden ? Çünkü döngü başladığı başka bir hücreyi bitirirse, hücre işaretçisinin nerede olacağını varsayamayız. Dürüst olmak gerekirse, bu uygulama beyni daha az beyinsiz yapar.


4
Harika, şimdi anladım :)
speeder

25
Bu, bu dil ideolojisini anlamaya çalışan acemi için mükemmel bir çözümdü. Tebrikler ve harika gönderi.
Casey

4
Gördüğüm en iyi Brainfuck girişi. Dürüst olmak gerekirse
Boyang

3
Boş zamanlarınız için bir projeye ihtiyacınız varsa, Brainfuck'a her zaman Unicode desteği ekleyebileceğinizi tahmin ediyorum.
Álvaro González

3
Gönderinizden sonra, BF artık! BF!
thanos.a

52

Wikipedia , kodun yorumlanmış bir versiyonuna sahiptir.

+++++ +++++             initialize counter (cell #0) to 10
[                       use loop to set the next four cells to 70/100/30/10
    > +++++ ++              add  7 to cell #1
    > +++++ +++++           add 10 to cell #2 
    > +++                   add  3 to cell #3
    > +                     add  1 to cell #4
    <<<< -                  decrement counter (cell #0)
]                   
> ++ .                  print 'H'
> + .                   print 'e'
+++++ ++ .              print 'l'
.                       print 'l'
+++ .                   print 'o'
> ++ .                  print ' '
<< +++++ +++++ +++++ .  print 'W'
> .                     print 'o'
+++ .                   print 'r'
----- - .               print 'l'
----- --- .             print 'd'
> + .                   print '!'
> .                     print '\n'

Sorularınızı yanıtlamak için G / Ç için ,ve .karakterleri kullanılır. Metin ASCII'dir.

Vikipedi makale de, biraz daha derinlemesine devam ediyor.

İlk satır a[0] = 10, 0'dan on kez artırılarak başlatılır . 2. satırdaki döngü, dizinin başlangıç ​​değerlerini etkin bir şekilde ayarlar: a[1] = 70(72'ye yakın, 'H' karakteri için ASCII kodu), a[2] = 100(101'e yakın veya 'e' ), a[3] = 30(32'ye yakın, boşluk kodu) ve a[4] = 10(yeni satır). Döngü 7, 10, 3, ve 1, hücrelere ilave çalışır a[1], a[2], a[3]ve a[4]10 eklenen her bir hücre için, toplam (veren - döngü içinde, sırasıyla, her zaman a[1]=70vb.) Döngü bittikten sonra a[0]sıfırdır. >++.daha sonra imleci a[1]70'i tutan, ona iki tane ekler (büyük H'nin ASCII karakter kodu olan 72'yi üretir) ve onu çıkarır.

Sonraki satır, dizi işaretçisini hareket ettirir ve a[2]ona bir ekler, daha sonra çıktı olan daha küçük bir 'e' olan 101'i üretir.

'L', 'e'den sonra yedinci harf olduğu için,' ll 'çıktısına yedi harf daha eklenir ( +++++++) a[2]ve sonuç iki kez çıktılanır.

'o', 'l'den sonraki üçüncü harftir, bu nedenle a[2]üç kez daha artırılır ve sonucu verir.

Programın geri kalanı da aynı şekilde devam ediyor. Boşluk ve büyük harfler için, farklı dizi hücreleri seçilir ve gerektiği gibi artırılır veya azaltılır.


Ama NEDEN yazdırıyor? veya nasıl? Yorumlar bana satırın amacını, şimdi ne işe yaradığını açıklıyor.
speeder

8
Yazdırır çünkü derleyici bunu bilir ,ve .G / Ç için kullanılır, tıpkı C kullanarak yazdırır gibi putchar. Derleyici tarafından işlenen bir uygulama detayıdır.
ken

1
Ayrıca "Merhaba Dünya" daki ASCII karakterleri için gerekli hücreleri tam sayı değerlerine ayarladığı için
slugonamission

Daha derinlemesine bir açıklama bekliyordum ... ama: /
speeder

1
@speeder - Wikipedia'daki kodun ayrıntılı açıklamasını yanıta ekledim. Daha fazla bilgi için bağlantılı makaleye bakabilirsiniz.
ken

9

Ne basacağını nasıl bildiği sorusuna cevap vermek için, ASCII değerlerinin hesaplamasını, baskının gerçekleştiği kodun sağına ekledim:

> just means move to the next cell
< just means move to the previous cell
+ and - are used for increment and decrement respectively. The value of the cell is updated when the increment/decrement happens

+++++ +++++             initialize counter (cell #0) to 10

[                       use loop to set the next four cells to 70/100/30/10

> +++++ ++              add  7 to cell #1

> +++++ +++++           add 10 to cell #2 

> +++                   add  3 to cell #3

> +                     add  1 to cell #4

<<<< -                  decrement counter (cell #0)

]            

> ++ .                  print 'H' (ascii: 70+2 = 72) //70 is value in current cell. The two +s increment the value of the current cell by 2

> + .                   print 'e' (ascii: 100+1 = 101)

+++++ ++ .              print 'l' (ascii: 101+7 = 108)

.                       print 'l' dot prints same thing again

+++ .                   print 'o' (ascii: 108+3 = 111)

> ++ .                  print ' ' (ascii: 30+2 = 32)

<< +++++ +++++ +++++ .  print 'W' (ascii: 72+15 = 87)

> .                     print 'o' (ascii: 111)

+++ .                   print 'r' (ascii: 111+3 = 114)

----- - .               print 'l' (ascii: 114-6 = 108)

----- --- .             print 'd' (ascii: 108-8 = 100)

> + .                   print '!' (ascii: 32+1 = 33)

> .                     print '\n'(ascii: 10)

9

Brainfuck adıyla aynı. Yalnızca 8 karakter kullanır, > [ . ] , - +bu da onu öğrenmesi en hızlı, ancak uygulaması ve anlaşılması en zor programlama dili yapar . … .Ve sonunda beyninizi siktirir.

Değerleri dizide saklar: [72] [101] [108] [111]

let, başlangıçta dizinin 1. hücresini gösteren işaretçi:

  1. > işaretçiyi 1 sağa taşı

  2. < işaretçiyi 1 sola taşı

  3. + hücrenin değerini 1 artır

  4. - elementin değerini 1 artır

  5. . mevcut hücrenin baskı değeri.

  6. , mevcut hücreye girdi al.

  7. [ ] döngü, +++ [-] 3 sayım sayacı bcz önünde 3 ′ + 've - değişkenleri 1 değer kadar düşürür.

hücrelerde depolanan değerler ascii değerleridir:

bu nedenle yukarıdaki diziye atıfta bulunarak: [72] [101] [108] [108] [111] ascii değerleriyle eşleşirseniz bunun Merhaba yazar olduğunu göreceksiniz

Tebrikler! BF'nin sözdizimini öğrendiniz

——- Daha fazlası ———

İlk programımızı yani Merhaba Dünya'yı yapalım , ardından adınızı bu dilde yazabilirsiniz.

+++++ +++++[> +++++ ++ >+++++ +++++ >+++ >+ <<<-]>++.>+.+++++ ++..+++.++.+++++ +++++ +++++.>.+++.----- -.----- ---.>+.>.

parçalara ayırmak:

+++++ +++++[> +++++ ++ 
                  >+++++ +++++ 
                  >+++ 
                  >+ 
                  <<<-]

4 hücrelik bir dizi oluşturur (> sayısı) ve şuna benzer 10'luk bir sayaç ayarlar: —-psuedo kodu—-

array =[7,10,3,1]
i=10
while i>0:
 element +=element
 i-=1

çünkü sayaç değeri 0 hücresinde saklanır ve> 1. hücreye gider, değerini + 7 ile günceller> 2. hücreye 10 artışla önceki değerine geçer ve böyle devam eder….

<<< 0 hücresine dön ve değerini 1 azaltır

dolayısıyla döngü tamamlandıktan sonra şu diziye sahibiz: [70,100,30,10]

>++. 

1. elemana gider ve değerini 2 (iki '+') artırır ve ardından bu ascii değeriyle ('.') karakterini yazdırır. örneğin python'da: chr (70 + 2) # 'H' yazdırır

>+.

2. hücre artımı 1'e 100 + 1 değerine geçer ve değerini ('.') yazdırır, yani chr (101) chr (101) # artık 'e' yazdırır sonraki parçada> veya <yoktur, böylece mevcut değeri alır en son eleman ve sadece ona artış

+++++ ++..

son eleman = 101 bu nedenle, 101 + 7 ve iki kez yazdırır (iki '..' olduğundan) chr (108) # baskılar l iki kez kullanılabilir

for i in array:
    for j in range(i.count(‘.’)):
           print_value

——— Nerelerde kullanılıyor? ---

Programcılara meydan okumak için yapılmış bir şaka dilidir ve pratik olarak hiçbir yerde kullanılmaz.


4

Tüm yanıtlar eksiksiz, ancak küçük bir ayrıntıdan yoksun: Baskı. Beyin fırtınası çevirmeninizi oluştururken, karakteri de göz önünde bulundurursunuz ., aslında beyin fırtınasında bir baskı ifadesi nasıl görünür. Yani beyinsiz çevirmeninizin yapması gereken şey, bir .karakterle karşılaştığında o anda işaret edilen baytı yazdırmasıdır.

Misal:

varsayalım ki -> char *ptr = [0] [0] [0] [97] [0]... eğer bu bir beyin fırtınası ifadesiyse: >>>.işaretçiniz 3 boşluk sağa inmeli: konumuna getirilmelidir [97], yani şimdi *ptr = 97, bunu yaptıktan sonra çevirmeniniz a ile karşılaşır ., sonra çağırmalıdır

write(1, ptr, 1)

veya herhangi bir eşdeğer baskı deyimi değerine sahiptir anda sivri byte, yazdırmak için 97 ve harf aardından yazdırılacaktır std_output.


1

Bence sorduğun, Brainfuck'ın tüm kodla ne yapacağını nasıl bildiği. Bir noktanın ne anlama geldiğini veya kodda bir toplama işaretinin ne anlama geldiğini yorumlamak için Python gibi daha yüksek seviyeli bir dilde yazılmış bir ayrıştırıcı vardır.

Böylelikle ayrıştırıcı kodunuzu satır satır okuyacak ve ok diyecek, bir> simgesi var, bu yüzden bellek konumunu ilerletmem gerekiyor, kod basitçe, eğer (bu bellek konumundaki içerik) ==>, memlocation = + memlocation daha yüksek seviyeli bir dilde yazılır, benzer şekilde if (bellek konumundaki içerik) == ".", ardından yazdırın (bellek konumunun içeriği).

Umarım bu sorunu çözer. tc

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.