Brainfuck हैलो वर्ल्ड वास्तव में कैसे काम करता है?


118

किसी ने इसे मेरे पास भेजा और दावा किया कि यह ब्रेनफक में एक हैलो दुनिया है (और मुझे आशा है कि ...)

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

मैं मूल बातें जानता हूं कि यह एक पॉइंटर और इंक्रीमेंट और डीक्रीमेंट सामान को स्थानांतरित करके काम करता है ...

फिर भी मैं अभी भी जानना चाहता हूं कि यह वास्तव में कैसे काम करता है? यह पहली बार स्क्रीन पर कुछ भी कैसे प्रिंट करता है? यह पाठ को कैसे एनकोड करता है? मुझे कुछ भी समझ नहीं आता...


31
इस भाषा में लिखे गए एप्लिकेशन को बनाए रखने के लिए बहुत कठिन होना चाहिए ..
e2-e4

17
@ ring0: nah, यह केवल लिखने की भाषा है।
LetMeSOThat4U

इसका व्यावहारिक उपयोग क्या है?
यश कुमार वर्मा

10
@ यशवर्मा को इसकी आवश्यकता नहीं है ..
पागल

49
@ यशवर्मा यह भाषा के नाम पर स्पष्ट रूप से निर्दिष्ट है।
मतीन उल्हाक

जवाबों:


255

1. मूल बातें

Brainfuck को समझने के लिए आपको 0प्रत्येक द्वारा आरम्भ की गई कोशिकाओं की अनंत सरणी की कल्पना करनी चाहिए ।

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

जब ब्रेनफक प्रोग्राम शुरू होता है, तो यह किसी भी सेल की ओर इशारा करता है।

...[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, जबकि aहै 97

खैर, अपने बीएफ कार्यक्रम की स्मृति की कल्पना की तरह दिखता है:

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

मान लिया जाए कि मानक इनपुट का अर्थ है a, यदि आप अल्पविराम ,ऑपरेटर का उपयोग करते हैं , तो BF मेमोरी में aASCII कोड क्या पढ़ता है 97:

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

आप आम तौर पर उस तरह से सोचना चाहते हैं, हालांकि सच्चाई थोड़ी अधिक जटिल है। सच्चाई यह है कि बीएफ एक चरित्र नहीं बल्कि एक बाइट (जो भी बाइट है) पढ़ता है। मैं आपको उदाहरण दिखाता हूं:

लिनक्स में

$ printf ł

प्रिंट:

ł

जो विशिष्ट पॉलिश चरित्र है। यह चरित्र ASCII एन्कोडिंग द्वारा एन्कोड नहीं किया गया है। इस मामले में यह UTF-8 एन्कोडिंग है, इसलिए यह कंप्यूटर मेमोरी में एक से अधिक बाइट लेता था। हम इसे हेक्साडेसिमल डंप बनाकर साबित कर सकते हैं:

$ printf ł | hd

जो दीखता है:

00000000  c5 82                                             |..|

जीरो ऑफसेट हैं। 82पहला है और c5दूसरा बाइट का प्रतिनिधित्व करता है ł(क्रम में हम उन्हें पढ़ेंगे)। |..|चित्रमय प्रतिनिधित्व है जो इस मामले में संभव नहीं है।

यदि आप łअपने बीएफ प्रोग्राम के इनपुट के रूप में पास होते हैं, जो सिंगल बाइट को पढ़ता है, तो प्रोग्राम मेमोरी इस तरह दिखाई देगी:

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

क्यों 197? अच्छी तरह से 197दशमलव c5हेक्साडेसिमल है। परिचित लगता है? बेशक। यह पहली बाइट है ł!

3. आउटपुट

वर्ण प्रिंट करने के लिए आप डॉट का उपयोग करते हैं, .जो यह है: मान लें कि हम वास्तविक सेल मान को दशमलव ASCII कोड की तरह मानते हैं, मानक चरित्र के अनुरूप चरित्र प्रिंट करें।

खैर, अपने बीएफ कार्यक्रम की स्मृति की कल्पना की तरह दिखता है:

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

यदि आप डॉट (?) ऑपरेटर का उपयोग करते हैं, तो BF क्या प्रिंट करता है:

क्योंकि aASCII में दशमलव कोड है 97

तो उदाहरण के लिए बीएफ प्रोग्राम इस तरह (97 प्लस 2 डॉट्स):

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

सेल के मूल्य में वृद्धि करेगा जो इसे 97 तक बताता है और इसे 2 बार प्रिंट करता है।

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)

हालाँकि जहाँ हमने शुरू किया था उसे समाप्त करना अच्छा है। क्यों ? क्योंकि यदि लूप किसी अन्य सेल को शुरू करता है, तो हम यह नहीं मान सकते कि सेल पॉइंटर कहां होगा। ईमानदार होने के लिए, यह अभ्यास दिमाग को कम दिमागदार बनाता है।


4
कूल, अब मैं इसे समझ गया :)
गति

25
इस भाषा विचारधारा को समझने की कोशिश करने वाले नौसिखियों के लिए यह एक सही समाधान था। बधाई, और शानदार पोस्ट।
केसी

4
बेस्ट ब्रेनफक इंट्रो मैंने देखा है। ईमानदारी से आप अपने पोस्ट द्वारा
बोफो को

3
मुझे लगता है कि यदि आपको अपने खाली समय के लिए एक परियोजना की आवश्यकता है, तो आप हमेशा ब्रेनफक में यूनिकोड समर्थन जोड़ सकते हैं।
अल्वारो गोंजालेज

3
आपकी पोस्ट के बाद, BF अब और 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 के करीब, वर्ण 'H' के लिए ASCII कोड), 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 का उत्पादन करता है, जो कि एक राजधानी एच का एएससीआईआई वर्ण कोड है), और इसे आउटपुट करता है।

अगली पंक्ति सरणी पॉइंटर को a[2]आगे बढ़ाती है और उसमें एक जोड़ देती है, जिससे 101 उत्पन्न होता है, एक लो-केस 'ई', जो तब आउटपुट होता है।

जैसा कि 'एल' 'ई' के बाद सातवाँ अक्षर होता है, आउटपुट के लिए 'एल' को एक और सात जोड़ा जाता है ( +++++++) a[2]और परिणाम दो बार आउटपुट होता है।

'ओ' 'एल' के बाद तीसरा अक्षर है, इसलिए a[2]तीन बार वृद्धि की जाती है और परिणाम का उत्पादन होता है।

बाकी कार्यक्रम उसी तरह से चलता है। अंतरिक्ष और बड़े अक्षरों के लिए, अलग-अलग सरणी कोशिकाओं का चयन किया जाता है और आवश्यकतानुसार बढ़ा या घटाया जाता है।


लेकिन यह प्रिंट क्यों करता है? या कैसे? टिप्पणियाँ मुझे लाइन का आशय समझा रही हैं, अब यह क्या करती है।
स्पीडर

8
यह प्रिंट करता है क्योंकि संकलक जानता है कि ,और .I / O के लिए उपयोग किया जाता है, बहुत सी प्रिंट की तरह उपयोग करके putchar। यह संकलक द्वारा संभाला जाने वाला कार्यान्वयन विवरण है।
केन

1
और इसलिए भी क्योंकि यह "हैलो वर्ल्ड" में ASCII पात्रों के लिए पूर्णांक मानों के लिए आवश्यक कोशिकाओं को सेट कर रहा है
slugonamission

मुझे गहराई से स्पष्टीकरण की अधिक उम्मीद थी ... लेकिन: /
स्पीडर

1
@speeder - मैंने विकिपीडिया से उत्तर में कोड की गहन व्याख्या जोड़ी। अधिक जानकारी के लिए आप लिंक किए गए लेख को देख सकते हैं।
केन

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

इसके नाम के समान ही ब्रेनफक । यह केवल 8 वर्णों का उपयोग करता है > [ . ] , - +जो इसे सीखने की सबसे तेज प्रोग्रामिंग भाषा बनाता है लेकिन इसे लागू करने और समझने में सबसे कठिन है … .और आपको अंत में अपने मस्तिष्क को ध्‍यान में रखते हुए f * बना देता है।

यह सरणी में मूल्यों को संग्रहीत करता है: [in२] [१०१] [१० [] [१११]

चलो, शुरू में सूचक सरणी के सेल 1 की ओर इशारा करते हैं:

  1. > सूचक को दाईं ओर ले जाएं

  2. < पॉइंटर को 1 से बाईं ओर ले जाएं

  3. + 1 से सेल के मूल्य में वृद्धि

  4. - 1 से तत्व के मूल्य में वृद्धि

  5. . वर्तमान सेल का प्रिंट मूल्य।

  6. , वर्तमान सेल में इनपुट लें।

  7. [ ] लूप, +++ [-] 3 काउंट bcz का काउंटर इससे पहले 3 before + 'है, और - decrements 1 मान से चर की गणना करते हैं।

कोशिकाओं में संग्रहीत मान एससीआई मान हैं:

इसलिए उपरोक्त सरणी का जिक्र करते हुए: [[२] [१०१] [१० [] [१० [] [१११] अगर आप अस्सी के मूल्यों से मेल खाते हैं, तो आप पाएंगे कि यह हैलो रिट्रीट है

बधाई! आपने BF का सिंटैक्स सीखा है

——— कुछ अधिक ———

आइए हम अपना पहला कार्यक्रम बनाते हैं , जिसका नाम है हैलो वर्ल्ड , जिसके बाद आप अपना नाम इस भाषा में लिख सकते हैं।

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

टुकड़े करना:

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

4 सेल (> की संख्या) की एक सरणी बनाता है और 10 के एक काउंटर की तरह कुछ सेट करता है:

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

क्योंकि काउंटर वैल्यू को सेल 0 में स्टोर किया जाता है और सेल 1 में ले जाता है, सेल 7 वैल्यू + 7> को सेल करता है, सेल 2 इंक्रीमेंट 10 को उसके पिछले वैल्यू और इसी तरह आगे बढ़ाता है।

<<< सेल 0 पर वापस लौटें और 1 से इसका मूल्य घटाएं

इसलिए लूप पूरा होने के बाद हमारे पास सरणी है: [70,100,30,10]

>++. 

1 तत्व पर ले जाता है और 2 (दो '+') और फिर उस एससीआई मूल्य के साथ प्रिंट ('') वर्ण द्वारा इसके मूल्य में वृद्धि करता है। उदाहरण के लिए अजगर में: chr (70 + 2) # प्रिंट 'H'

>+.

2 सेल वृद्धि 1 से इसके मूल्य 100 + 1 और प्रिंट्स ('।') तक ले जाता है, इसका मूल्य यानी chr (101) chr (101) # चिह्न 'e' अब अगले भाग में कोई> या <नहीं है, इसलिए यह वर्तमान मान लेता है केवल नवीनतम तत्व और इसे बढ़ाने के लिए

+++++ ++..

नवीनतम तत्व = 101 इसलिए, 101 + 7 और इसे दो बार प्रिंट करता है (जैसा कि दो '..') chr (108) # चिह्न l दो बार इस्तेमाल किया जा सकता है

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

—— यह कहाँ उपयोग किया जाता है? ---

यह सिर्फ एक मजाक की भाषा है जो प्रोग्रामर को चुनौती देने के लिए बनाई गई है और कहीं भी व्यावहारिक रूप से इसका उपयोग नहीं किया जाता है।


4

सभी उत्तर पूरी तरह से हैं, लेकिन उनमें एक छोटे से विस्तार की कमी है: मुद्रण। अपने ब्रेनफैक अनुवादक का निर्माण करने में, आप चरित्र पर भी विचार करते हैं ., यह वास्तव में वही है जो एक स्टेटमेंट स्टेटमेंट ब्रेनफॉक में दिखता है। तो आपका ब्रेनफैक अनुवादक क्या करना चाहिए, जब भी यह एक .चरित्र का सामना करता है तो यह वर्तमान में इंगित बाइट को प्रिंट करता है।

उदाहरण:

मान लीजिए कि आपके पास -> char *ptr = [0] [0] [0] [97] [0]... यदि यह एक दिमागी कथन है: >>>.आपके पॉइंटर को सही लैंडिंग के लिए 3 रिक्त स्थान ले जाना चाहिए: [97]तो *ptr = 97, अब , ऐसा करने के बाद कि आपका अनुवादक ए का सामना करता है ., तो उसे कॉल करना चाहिए

write(1, ptr, 1)

या वर्तमान में बताए गए बाइट को प्रिंट करने के लिए किसी भी समान प्रिंटिंग स्टेटमेंट, जिसका मूल्य 97 है और aउसके बाद पत्र मुद्रित किया जाएगा std_output


1

मुझे लगता है कि आप जो पूछ रहे हैं वह ब्रेनफक कैसे जानता है कि सभी कोड के साथ क्या करना है। एक उच्च स्तरीय भाषा में एक पार्सर लिखा गया है जैसे कि एक बिंदु का अर्थ क्या है, या कोड में एक अतिरिक्त चिह्न का क्या अर्थ है, यह समझाने के लिए पायथन।

तो पार्सर लाइन द्वारा आपकी कोड लाइन पढ़ेगा, और कहेगा कि ठीक है> प्रतीक है इसलिए मुझे मेमोरी लोकेशन को एडवांस करना है, कोड बस है, अगर (उस मेमोरी लोकेशन में कंटेंट) ==>, मेमलोकेशन = + मेम्लोकेशन जो है उच्च स्तर की भाषा में लिखा गया है, इसी तरह यदि (मेमोरी लोकेशन में सामग्री) == "।", तो प्रिंट करें (मेमोरी लोकेशन की सामग्री)।

आशा है कि यह इसे साफ करता है। टीसी

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.