Brainfuck Hello World ทำงานอย่างไร


118

มีคนส่งสิ่งนี้มาให้ฉันและอ้างว่าเป็นสวัสดีชาวโลกใน Brainfuck (และฉันหวังว่าอย่างนั้น ... )

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

ฉันรู้พื้นฐานว่ามันทำงานได้โดยการเลื่อนตัวชี้และเพิ่มและลดสิ่งต่างๆ ...

แต่ฉันก็ยังอยากรู้ว่ามันทำงานอย่างไร? มันพิมพ์อะไรบนหน้าจอได้อย่างไรในตอนแรก? มันเข้ารหัสข้อความอย่างไร? ไม่เข้าใจเลย ...


31
ต้องค่อนข้างยากที่จะดูแลแอปพลิเคชันที่เขียนด้วยภาษานี้ ..
e2-e4

18
@ ring0: ไม่เป็นภาษาเขียนอย่างเดียว
LetMeSOThat4U

ใช้งานได้จริงคืออะไร?
Yash Kumar Verma

10
@YashVerma ไม่จำเป็นต้องมี ..
บ้า

49
@YashVerma มันระบุชื่อภาษาไว้ชัดเจน
Mateen Ulhaq

คำตอบ:


255

1. พื้นฐาน

เพื่อให้เข้าใจ Brainfuck คุณต้องจินตนาการถึงอาร์เรย์ของเซลล์ที่เริ่มต้นโดย0แต่ละเซลล์

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

เมื่อโปรแกรม Brainfuck เริ่มต้นจะชี้ไปที่เซลล์ใด ๆ

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

หากคุณเลื่อนตัวชี้ไปทางขวาแสดง>ว่าคุณกำลังย้ายตัวชี้จากเซลล์ X ไปยังเซลล์ X + 1

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

หากคุณเพิ่มมูลค่าเซลล์+คุณจะได้รับ:

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

หากคุณเพิ่มมูลค่าเซลล์อีกครั้ง+คุณจะได้รับ:

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

หากคุณลดค่าเซลล์-คุณจะได้รับ:

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

หากคุณย้ายตัวชี้ไปทางซ้ายแสดง<ว่าคุณกำลังย้ายตัวชี้จากเซลล์ X ไปยังเซลล์ X-1

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

2. อินพุต

,หากต้องการอ่านตัวอักษรที่คุณใช้เครื่องหมายจุลภาค มันทำอะไร: อ่านอักขระจากอินพุตมาตรฐานและเขียนรหัส ASCII ทศนิยมไปยังเซลล์จริง

ลองดูที่ตาราง ASCII ตัวอย่างเช่นรหัสทศนิยม!คือ33ในขณะที่มีa97

ลองนึกภาพหน่วยความจำโปรแกรม BF ของคุณเป็นดังนี้:

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

สมมติว่าอินพุตมาตรฐานย่อมาจากaถ้าคุณใช้,ตัวดำเนินการลูกน้ำสิ่งที่ BF ทำคืออ่านaรหัส ASCII ทศนิยม97ไปยังหน่วยความจำ:

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

โดยทั่วไปคุณอยากจะคิดแบบนั้นอย่างไรก็ตามความจริงนั้นซับซ้อนกว่าเล็กน้อย ความจริงก็คือ BF ไม่ได้อ่านอักขระ แต่เป็นไบต์ (ไบต์คืออะไร) ให้ฉันดูตัวอย่าง:

ในลินุกซ์

$ printf ł

พิมพ์:

ł

ซึ่งเป็นลักษณะเฉพาะของโปแลนด์ อักขระนี้ไม่ได้เข้ารหัสโดยการเข้ารหัส ASCII ในกรณีนี้เป็นการเข้ารหัส UTF-8 ดังนั้นจึงเคยใช้หน่วยความจำคอมพิวเตอร์มากกว่าหนึ่งไบต์ เราสามารถพิสูจน์ได้โดยการถ่ายโอนข้อมูลฐานสิบหก:

$ printf ł | hd

ซึ่งแสดง:

00000000  c5 82                                             |..|

ศูนย์จะถูกชดเชย 82เป็นc5ไบต์แรกและเป็นไบต์ที่สองแทนł(ตามลำดับเราจะอ่าน) |..|เป็นการแสดงกราฟิกซึ่งไม่สามารถทำได้ในกรณีนี้

ถ้าคุณส่งłอินพุตไปยังโปรแกรม BF ของคุณที่อ่านไบต์เดียวหน่วยความจำโปรแกรมจะมีลักษณะดังนี้:

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

ทำไม197? 197ทศนิยมดีเป็นc5เลขฐานสิบหก ดูเหมือนจะคุ้นเคย? แน่นอน. เป็นไบต์แรกของł!

3. เอาต์พุต

ในการพิมพ์อักขระที่คุณใช้จุด.คืออะไร: สมมติว่าเราปฏิบัติตามค่าของเซลล์จริงเช่นรหัส ASCII ทศนิยมให้พิมพ์อักขระที่สอดคล้องกับเอาต์พุตมาตรฐาน

ลองนึกภาพหน่วยความจำโปรแกรม BF ของคุณเป็นดังนี้:

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

หากคุณใช้ตัวดำเนินการ dot (.) ในตอนนี้สิ่งที่ BF ทำคือพิมพ์:

a

เพราะaรหัสทศนิยมใน ASCII 97คือ

ตัวอย่างเช่นโปรแกรม BF เช่นนี้ (97 บวก 2 จุด):

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

จะเพิ่มมูลค่าของเซลล์ที่ชี้ได้ถึง 97 และพิมพ์ออกมา 2 ครั้ง

AA

4. ลูป

ใน BF วงประกอบด้วยวงเริ่มต้นและจุดสิ้นสุดห่วง[ ]คุณสามารถคิดว่ามันเหมือนกับใน C / C ++ ที่เงื่อนไขคือค่าเซลล์จริง

ดูโปรแกรม BF ด้านล่าง:

++[]

++ เพิ่มค่าเซลล์จริงสองครั้ง:

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

และ[]ก็เหมือนกับwhile(2) {}มันวนซ้ำไม่สิ้นสุด

สมมติว่าเราไม่ต้องการให้ลูปนี้ไม่มีที่สิ้นสุด เราสามารถทำได้เช่น:

++[-]

ดังนั้นทุกครั้งที่ลูปวนซ้ำมันจะลดค่าของเซลล์จริง เมื่อค่าของเซลล์จริง0สิ้นสุดการวนรอบ:

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

ลองพิจารณาอีกตัวอย่างหนึ่งของวง จำกัด :

++[>]

ตัวอย่างนี้แสดงให้เห็นว่าเรายังไม่เสร็จสิ้นการวนซ้ำที่เซลล์ที่ลูปเริ่มต้น:

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

อย่างไรก็ตามเป็นแนวทางปฏิบัติที่ดีที่จะยุติจุดเริ่มต้น ทำไม? เนื่องจากถ้าลูปสิ้นสุดเซลล์อื่นเซลล์เริ่มต้นเราไม่สามารถสันนิษฐานได้ว่าตัวชี้เซลล์จะอยู่ที่ใด พูดตามตรงการปฏิบัตินี้ทำให้ Brainfuck สมองน้อยลง


4
เจ๋งตอนนี้ฉันเข้าใจแล้ว :)
speeder

25
นั่นเป็นวิธีแก้ปัญหาที่สมบูรณ์แบบสำหรับมือใหม่ที่พยายามเข้าใจอุดมการณ์ทางภาษานี้ ขอแสดงความยินดีและโพสต์ที่ดี
Casey

4
แนะนำ Brainfuck ที่ดีที่สุดที่ฉันเคยเห็น สุจริตคุณเลิกทำ BF สักหน่อยโดยโพสต์ของคุณ
Boyang

3
ฉันเดาว่าหากคุณต้องการโครงการสำหรับเวลาว่างคุณสามารถเพิ่มการสนับสนุน Unicode ให้กับ Brainfuck ได้ตลอดเวลา
ÁlvaroGonzález

3
หลังจากโพสต์ของคุณ BF is! BF อีกต่อไป!
thanos.a

52

วิกิพีเดียมีโค้ดเวอร์ชันแสดงความคิดเห็น

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

เพื่อตอบคำถามของคุณอักขระ,และ.จะใช้สำหรับ I / O ข้อความคือ ASCII

วิกิพีเดียบทความที่เกิดขึ้นในบางความลึกมากขึ้นเช่นเดียวกับ

บรรทัดแรกเริ่มต้นa[0] = 10ด้วยการเพิ่มสิบครั้งจาก 0 ลูปจากบรรทัดที่ 2 ตั้งค่าเริ่มต้นสำหรับอาร์เรย์ได้อย่างมีประสิทธิภาพ: a[1] = 70(ใกล้ถึง 72 รหัส ASCII สำหรับอักขระ 'H') a[2] = 100(ใกล้กับ 101 หรือ 'e' ), a[3] = 30(ใกล้ถึง 32 รหัสสำหรับช่องว่าง) และa[4] = 10(ขึ้นบรรทัดใหม่) ห่วงทำงานโดยเพิ่ม 7, 10, 3 และ 1 ไปยังเซลล์a[1], a[2], a[3]และa[4]ตามลำดับในแต่ละครั้งที่ผ่านห่วง - 10 เพิ่มเติมสำหรับแต่ละเซลล์ในรวม (ให้ a[1]=70ฯลฯ ) หลังจากการวนซ้ำเสร็จสิ้นa[0]เป็นศูนย์ >++.จากนั้นย้ายตัวชี้ไปa[1]ที่ซึ่งมี 70 เพิ่มสองตัว (การผลิต 72 ซึ่งเป็นรหัสอักขระ ASCII ของตัวพิมพ์ใหญ่ H) และส่งออก

บรรทัดถัดไปจะย้ายตัวชี้อาร์เรย์ไปที่a[2]และเพิ่มตัวชี้เข้าไปโดยสร้าง 101 เป็นตัวพิมพ์เล็ก 'e' ซึ่งจะถูกส่งออก

เนื่องจาก 'l' เป็นตัวอักษรตัวที่เจ็ดหลัง 'e' เพื่อเอาท์พุท 'll' อีกเจ็ดตัวจะถูกเพิ่ม ( +++++++) ถึงa[2]และผลลัพธ์จะออกเป็นสองเท่า

'o' เป็นอักษรตัวที่สามหลัง 'l' ดังนั้นจึงa[2]เพิ่มขึ้นอีกสามครั้งและแสดงผลลัพธ์

ส่วนที่เหลือของโปรแกรมจะดำเนินไปในลักษณะเดียวกัน สำหรับช่องว่างและตัวพิมพ์ใหญ่เซลล์อาร์เรย์ต่างๆจะถูกเลือกและเพิ่มหรือลดตามต้องการ


แต่ทำไมถึงพิมพ์? หรืออย่างไร ความคิดเห็นกำลังอธิบายให้ฉันทราบถึงเจตนาของสายตอนนี้ว่ามันทำอะไร
speeder

8
มันพิมพ์เพราะคอมไพเลอร์รู้,และ.ใช้สำหรับ I / O เหมือนกับ C พิมพ์โดยใช้putchar. เป็นรายละเอียดการใช้งานที่จัดการโดยคอมไพเลอร์
ken

1
และเนื่องจากเป็นการตั้งค่าเซลล์ที่ต้องการเป็นค่าจำนวนเต็มสำหรับอักขระ ASCII ใน "Hello World"
slugonamission

ฉันคาดหวังคำอธิบายเชิงลึกมากกว่านี้ ... แต่: /
speeder

1
@speeder - ฉันเพิ่มคำอธิบายเชิงลึกของโค้ดจาก Wikipedia ลงในคำตอบ คุณสามารถดูบทความที่เชื่อมโยงสำหรับข้อมูลเพิ่มเติม
ken

9

เพื่อตอบคำถามว่ามันรู้ได้อย่างไรว่าจะพิมพ์อะไรฉันได้เพิ่มการคำนวณค่า ASCII ที่ด้านขวาของรหัสที่เกิดการพิมพ์:

> 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 เหมือนกับชื่อของมัน ใช้อักขระเพียง 8 ตัว> [ . ] , - +ซึ่งทำให้เป็นภาษาการเขียนโปรแกรมที่เร็วที่สุดในการเรียนรู้แต่ใช้งานและทำความเข้าใจได้ยากที่สุด …. และในที่สุดก็ทำให้คุณได้สมองฝ่อ

เก็บค่าในอาร์เรย์: [72] [101] [108] [111]

ให้ตัวชี้เริ่มต้นชี้ไปที่เซลล์ 1 ของอาร์เรย์:

  1. > เลื่อนตัวชี้ไปทางขวาโดย 1

  2. < เลื่อนตัวชี้ไปทางซ้าย 1

  3. + เพิ่มมูลค่าของเซลล์ทีละ 1

  4. - เพิ่มมูลค่าขององค์ประกอบโดย 1

  5. . พิมพ์ค่าของเซลล์ปัจจุบัน

  6. , ป้อนข้อมูลไปยังเซลล์ปัจจุบัน

  7. [ ] loop, +++ [-] ตัวนับ 3 ตัว bcz มี 3 ′+' อยู่ข้างหน้าและ - จำนวนที่ลดลงจะนับตัวแปรด้วย 1 ค่า

ค่าที่เก็บไว้ในเซลล์คือค่า ascii:

ดังนั้นการอ้างถึงอาร์เรย์ด้านบน: [72] [101] [108] [108] [111] หากคุณจับคู่ค่า ascii คุณจะพบว่าเป็นHello Wrterntern

ยินดีด้วย! คุณได้เรียนรู้ไวยากรณ์ของ BF แล้ว

——- บางอย่างเพิ่มเติม ———

ให้เราสร้างโปรแกรมแรกของเราคือHello Worldหลังจากนั้นคุณก็สามารถเขียนชื่อของคุณเป็นภาษานี้ได้

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

แตกเป็นชิ้น ๆ :

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

สร้างอาร์เรย์ 4 เซลล์ (จำนวน>) และตั้งค่าตัวนับ 10 อย่างเช่น: —-psuedo code—-

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

เนื่องจากค่าตัวนับถูกเก็บไว้ในเซลล์ 0 และ> ย้ายไปยังเซลล์ 1 จะอัปเดตค่าโดย + 7> ย้ายไปยังเซลล์ 2 เพิ่มขึ้น 10 เป็นค่าก่อนหน้าและอื่น ๆ ...

<<< กลับไปที่เซลล์ 0 และลดค่าลง 1

ดังนั้นหลังจากการวนรอบเสร็จสิ้นเรามีอาร์เรย์: [70,100,30,10]

>++. 

ย้ายไปยังองค์ประกอบที่ 1 และเพิ่มค่าด้วย 2 (สอง '+') จากนั้นพิมพ์อักขระ (".") ด้วยค่า ascii นั้น เช่นใน python: chr (70 + 2) # พิมพ์ 'H'

>+.

ย้ายไปที่เซลล์ที่ 2 เพิ่มขึ้น 1 เป็นค่า 100 + 1 และพิมพ์ (".") ค่าของมันคือ chr (101) chr (101) #prints 'e' ตอนนี้ไม่มี> หรือ <ในชิ้นถัดไปดังนั้นจึงใช้มูลค่าปัจจุบัน ขององค์ประกอบล่าสุดและเพิ่มขึ้นเท่านั้น

+++++ ++..

องค์ประกอบล่าสุด = 101 ดังนั้น 101 + 7 และพิมพ์สองครั้ง (เนื่องจากมีสอง ".. ") chr (108) #prints l สองครั้งสามารถใช้เป็น

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

——— ใช้ที่ไหน? ---

มันเป็นเพียงภาษาตลกที่สร้างขึ้นเพื่อท้าทายโปรแกรมเมอร์และไม่ได้ใช้จริงทุกที่


4

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

ตัวอย่าง:

สมมติว่าคุณมี -> char *ptr = [0] [0] [0] [97] [0]... ถ้านี่เป็นคำสั่งลับสมอง: >>>.ตัวชี้ของคุณควรถูกย้าย 3 ช่องว่างเพื่อลงจอดที่: [97]ดังนั้นตอนนี้*ptr = 97หลังจากที่นักแปลของคุณพบ a .แล้วก็ควรเรียก

write(1, ptr, 1)

หรือคำสั่งการพิมพ์ที่เทียบเท่าเพื่อพิมพ์ไบต์ที่ชี้อยู่ในปัจจุบันซึ่งมีค่า97aจากนั้นตัวอักษรจะถูกพิมพ์บนไฟล์std_output.


1

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

ดังนั้นโปรแกรมแยกวิเคราะห์จะอ่านโค้ดของคุณทีละบรรทัดและบอกว่าตกลงมีสัญลักษณ์> ดังนั้นฉันต้องเลื่อนตำแหน่งหน่วยความจำรหัสก็คือถ้า (เนื้อหาในตำแหน่งหน่วยความจำนั้น) ==>, memlocation = + memlocation ซึ่งก็คือ เขียนด้วยภาษาระดับสูงในทำนองเดียวกันถ้า (เนื้อหาในตำแหน่งหน่วยความจำ) == "." จากนั้นพิมพ์ (เนื้อหาของตำแหน่งหน่วยความจำ)

หวังว่านี่จะกระจ่างขึ้น TC

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.