Bagaimana sebenarnya Brainfuck Hello World bekerja?

118

Seseorang mengirimkan ini kepada saya dan mengklaim itu adalah halo dunia di Brainfuck (dan saya harap begitu ...)

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

Saya tahu dasar-dasar cara kerjanya dengan memindahkan pointer dan increment dan mengurangi barang ...

Namun saya masih ingin tahu, bagaimana sebenarnya cara kerjanya? Bagaimana cara mencetak sesuatu di layar pada awalnya? Bagaimana cara menyandikan teks? Saya tidak mengerti sama sekali ...

speeder
sumber
31
Pasti cukup sulit untuk memelihara aplikasi yang ditulis dalam bahasa ini ..
e2-e4
17
@ ring0: nah, itu bahasa hanya-tulis.
LetMeSOThat4U
apa kegunaan praktisnya?
Yash Kumar Verma
10
@YashVerma tidak membutuhkannya ..
Gila
49
@YashVerma Ini jelas ditentukan dalam nama bahasa.
Mateen Ulhaq

Jawaban:

255

1. Dasar-dasar

Untuk memahami Brainfuck, Anda harus membayangkan deretan sel tak terbatas yang diinisialisasi oleh 0masing-masing sel .

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

Saat program brainfuck dimulai, itu menunjuk ke sel mana saja.

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

Jika Anda memindahkan penunjuk ke kanan, >Anda memindahkan penunjuk dari sel X ke sel X + 1

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

Jika Anda meningkatkan nilai sel, +Anda mendapatkan:

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

Jika Anda meningkatkan nilai sel lagi, +Anda mendapatkan:

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

Jika Anda menurunkan nilai sel, -Anda mendapatkan:

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

Jika Anda memindahkan penunjuk ke kiri, <Anda memindahkan penunjuk dari sel X ke sel X-1

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

2. Masukan

Untuk membaca karakter Anda menggunakan koma ,. Apa yang dilakukannya adalah: Membaca karakter dari input standar dan menulis kode ASCII desimalnya ke sel yang sebenarnya.

Lihat tabel ASCII . Misalnya, kode desimal !is 33, while ais 97.

Nah, bayangkan memori program BF Anda terlihat seperti:

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

Dengan asumsi input standar singkatan a, jika Anda menggunakan ,operator koma , yang dilakukan BF adalah membaca akode ASCII desimal 97ke memori:

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

Anda biasanya ingin berpikir seperti itu, namun kenyataannya sedikit lebih kompleks. Sebenarnya BF tidak membaca karakter tapi byte (apapun byte itu). Mari saya tunjukkan contoh:

Di linux

$ printf ł

cetakan:

ł

yang merupakan karakter Polandia tertentu. Karakter ini tidak dikodekan oleh pengkodean ASCII. Dalam hal ini adalah pengkodean UTF-8, jadi biasanya memakan waktu lebih dari satu byte dalam memori komputer. Kami dapat membuktikannya dengan membuat dump heksadesimal:

$ printf ł | hd

yang menunjukkan:

00000000  c5 82                                             |..|

Angka nol diimbangi. 82adalah yang pertama dan c5mewakili byte kedua ł(agar kita akan membacanya). |..|adalah representasi grafis yang tidak dimungkinkan dalam kasus ini.

Nah, jika Anda mengirimkan łsebagai masukan ke program BF Anda yang membaca satu byte, memori program akan terlihat seperti:

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

Kenapa 197? Nah 197desimal adalah c5heksadesimal. Tampak akrab? Tentu saja. Ini byte pertama dari ł!

3. Keluaran

Untuk mencetak karakter Anda menggunakan titik .Apa yang dilakukannya adalah: Dengan asumsi kita memperlakukan nilai sel aktual seperti kode ASCII desimal, mencetak karakter yang sesuai dengan keluaran standar.

Nah, bayangkan memori program BF Anda terlihat seperti:

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

Jika Anda menggunakan operator titik (.) Sekarang, yang dilakukan BF adalah mencetak:

Sebuah

Karena akode desimal di ASCII adalah 97.

Jadi misal program BF seperti ini (97 plus 2 titik):

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

Akan meningkatkan nilai sel yang ditunjuknya hingga 97 dan mencetaknya 2 kali.

A A

4. Perulangan

Dalam BF loop terdiri dari loop begin [dan loop end ]. Anda bisa menganggapnya seperti saat berada di C / C ++ di mana kondisinya adalah nilai sel sebenarnya.

Lihat program BF di bawah ini:

++[]

++ menambah nilai sel sebenarnya dua kali:

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

Dan []seperti while(2) {}, jadi ini putaran tak terbatas.

Katakanlah kita tidak ingin putaran ini tidak terbatas. Kita bisa lakukan misalnya:

++[-]

Jadi setiap kali loop loop itu menurunkan nilai sel yang sebenarnya. Setelah nilai sel sebenarnya adalah 0loop berakhir:

...[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)

Mari kita pertimbangkan contoh lain dari loop terbatas:

++[>]

Contoh ini menunjukkan, kita belum menyelesaikan loop di sel tempat loop dimulai:

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

Bagaimanapun juga merupakan praktik yang baik untuk mengakhiri dari mana kita memulai. Kenapa? Karena jika loop mengakhiri sel lain itu dimulai, kita tidak bisa berasumsi di mana penunjuk sel akan berada. Sejujurnya, praktik ini mengurangi brainfuck.

Scony
sumber
4
Keren, sekarang saya mengerti :)
speeder
25
Itu adalah solusi sempurna bagi pemula yang mencoba memahami ideologi bahasa ini. Selamat, dan postingan bagus.
Casey
4
Intro Brainfuck terbaik yang pernah saya lihat. Jujur saja Anda membatalkan BF sedikit melalui pos Anda
Boyang
3
Saya rasa jika Anda membutuhkan proyek untuk waktu luang, Anda selalu dapat menambahkan dukungan Unicode ke Brainfuck.
Álvaro González
3
Setelah posting Anda, BF adalah! BF lagi!
thanos.a
52

Wikipedia memiliki versi kode yang diberi komentar.

+++++ +++++             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'

Untuk menjawab pertanyaan Anda, karakter ,dan .digunakan untuk I / O. Teksnya adalah ASCII.

The Wikipedia Artikel yang terjadi di beberapa lebih mendalam, juga.

Baris pertama diinisialisasi a[0] = 10hanya dengan menambah sepuluh kali dari 0. Loop dari baris 2 secara efektif menetapkan nilai awal untuk array: a[1] = 70(mendekati 72, kode ASCII untuk karakter 'H'), a[2] = 100(mendekati 101 atau 'e' ), a[3] = 30(mendekati 32, kode untuk spasi) dan a[4] = 10(baris baru). Loop bekerja dengan menambahkan 7, 10, 3, dan 1, sel-sel a[1], a[2], a[3]dan a[4]masing-masing setiap kali melalui loop - 10 tambahan untuk setiap sel total (memberikan a[1]=70dll). Setelah loop selesai, a[0]adalah nol. >++.kemudian memindahkan penunjuk ke a[1], yang menampung 70, menambahkan dua padanya (menghasilkan 72, yang merupakan kode karakter ASCII dari huruf besar H), dan mengeluarkannya.

Baris berikutnya memindahkan pointer array ke a[2]dan menambahkan satu padanya, menghasilkan 101, huruf kecil 'e', ​​yang kemudian menjadi keluaran.

Karena 'l' kebetulan menjadi huruf ketujuh setelah 'e', ​​untuk mengeluarkan 'll' tujuh lainnya ditambahkan ( +++++++) ke a[2]dan hasilnya adalah keluaran dua kali.

'o' adalah huruf ketiga setelah 'l', sehingga a[2]bertambah tiga kali lipat dan menampilkan hasilnya.

Program lainnya berjalan dengan cara yang sama. Untuk spasi dan huruf kapital, sel array yang berbeda dipilih dan ditambah atau dikurangi sesuai kebutuhan.

ken
sumber
Tapi KENAPA dicetak? atau bagaimana? Komentar tersebut menjelaskan kepada saya maksud dari baris tersebut, sekarang apa fungsinya.
speeder
8
Ini dicetak karena kompiler tahu itu ,dan .digunakan untuk I / O, seperti C print dengan menggunakan putchar. Ini adalah detail implementasi yang ditangani oleh kompilator.
ken
1
Dan juga karena itu menyetel sel yang diperlukan ke nilai integer untuk karakter ASCII di "Hello World"
slugonamission
Saya harapkan lebih dalam penjelasan mendalam ... tapi: /
speeder
1
@ speeder - Saya menambahkan penjelasan mendalam tentang kode dari Wikipedia ke jawabannya. Anda dapat melihat artikel yang ditautkan untuk informasi lebih lanjut.
ken
9

Untuk menjawab pertanyaan bagaimana ia tahu apa yang harus dicetak, saya telah menambahkan kalkulasi nilai ASCII di sebelah kanan kode tempat pencetakan terjadi:

> 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)
Rehana Mahfuz
sumber
9

Brainfuck sama seperti namanya. Ini hanya menggunakan 8 karakter > [ . ] , - +yang menjadikannya bahasa pemrograman tercepat untuk dipelajari tetapi paling sulit untuk diterapkan dan dipahami. … .Dan membuat Anda akhirnya berakhir dengan mengacaukan otak Anda.

Ini menyimpan nilai dalam array: [72] [101] [108] [111]

let, awalnya pointer menunjuk ke sel 1 dari array:

  1. > pindahkan penunjuk ke kanan sebanyak 1

  2. < pindahkan penunjuk ke kiri sebesar 1

  3. + menambah nilai sel sebesar 1

  4. - menambah nilai elemen sebesar 1

  5. . nilai cetak sel saat ini.

  6. , mengambil masukan ke sel saat ini.

  7. [ ] loop, +++ [-] penghitung 3 hitungan bcz memiliki 3 '+' sebelumnya, dan - mengurangi variabel hitungan dengan 1 nilai.

nilai yang disimpan dalam sel adalah nilai ascii:

jadi mengacu pada larik di atas: [72] [101] [108] [108] [111] jika Anda cocok dengan nilai ascii Anda akan menemukan bahwa itu adalah Hello writtern

Selamat! Anda telah mempelajari sintaks BF

——- Sesuatu yang lebih ———

mari kita buat program pertama kita yaitu Hello World , setelah itu Anda bisa menuliskan nama Anda dalam bahasa ini.

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

pecah menjadi beberapa bagian:

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

Membuat array dari 4 sel (jumlah>) dan menetapkan penghitung 10 sesuatu seperti: —-psuedo code—-

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

karena nilai counter disimpan di cell 0 dan> pindah ke cell 1 update nilainya dengan + 7> pindah ke cell 2 selisih 10 ke nilai sebelumnya dan seterusnya….

<<< kembali ke sel 0 dan mengurangi nilainya dengan 1

maka setelah penyelesaian loop kita memiliki array: [70,100,30,10]

>++. 

berpindah ke elemen pertama dan menambah nilainya dengan 2 (dua '+') dan kemudian mencetak karakter ('.') dengan nilai ascii tersebut. yaitu misalnya di python: chr (70 + 2) # cetakan 'H'

>+.

pindah ke sel ke-2 selisih 1 ke nilainya 100 + 1 dan mencetak ('.') nilainya yaitu chr (101) chr (101) #prints 'e' sekarang tidak ada> atau <di bagian berikutnya sehingga mengambil nilai sekarang dari elemen terbaru dan kenaikannya saja

+++++ ++..

elemen terbaru = 101 oleh karena itu, 101 + 7 dan mencetaknya dua kali (karena ada dua '..') chr (108) #prints l dua kali dapat digunakan sebagai

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

——— Dimana itu digunakan? ——-

Ini hanyalah bahasa lelucon yang dibuat untuk menantang pemrogram dan tidak digunakan secara praktis di mana pun.

DARK_C0D3R
sumber
4

Semua jawaban lengkap, tapi kurang satu detail kecil: Pencetakan. Dalam membangun penerjemah brainfuck, Anda juga mempertimbangkan karakternya ., seperti inilah sebenarnya tampilan printing statement di brainfuck. Jadi apa yang harus dilakukan penerjemah brainfuck Anda adalah, setiap kali ia menemukan .karakter itu mencetak byte yang saat ini menunjuk.

Contoh:

misalkan Anda memiliki -> char *ptr = [0] [0] [0] [97] [0]... jika ini adalah pernyataan brainfuck: >>>.penunjuk Anda harus dipindahkan 3 spasi ke kanan mendarat di:, [97]jadi sekarang *ptr = 97, setelah melakukan itu penerjemah Anda menemukan a ., itu kemudian harus memanggil

write(1, ptr, 1)

atau pernyataan pencetakan yang setara untuk mencetak byte yang saat ini menunjuk, yang memiliki nilai 97 dan huruf akemudian akan dicetak pada std_output.

rapdean
sumber
1

Saya pikir yang Anda tanyakan adalah bagaimana Brainfuck tahu apa yang harus dilakukan dengan semua kode. Ada pengurai yang ditulis dalam bahasa tingkat yang lebih tinggi seperti Python untuk menafsirkan arti titik, atau arti tanda tambahan dalam kode.

Jadi parser akan membaca kode Anda baris demi baris, dan mengatakan oke ada simbol> jadi saya harus memajukan lokasi memori, kodenya sederhana, jika (isi di lokasi memori itu) ==>, memlocation = + memlocation yang mana ditulis dalam bahasa tingkat yang lebih tinggi, demikian pula jika (konten di lokasi memori) == ".", lalu cetak (konten lokasi memori).

Semoga ini menyelesaikannya. tc

Rahul
sumber