ट्रैक
/
Factor
Factor
/
अभ्यास
/
संकेत स्टेंसिल
संकेत स्टेंसिल

संकेत स्टेंसिल

सीखने का अभ्यास

परिचय

आप पहले से जानते हैं कि टुकड़ों से एक कोटेशन कैसे बनाया जाता है और उसे कैसे चलाया जाता है। कोटेशन असल में कोड का ही एक सीक्वेंस होता है, इसलिए जिन सीक्वेंस वर्ड को आप जानते हैं, वे सब उस पर लागू होते हैं:

USING: kernel math prettyprint quotations sequences ;

{ [ 1 ] [ 2 + ] [ 3 * ] } concat >quotation call .   ! => 9
[ 1 + ] dup append .                                 ! => [ 1 + 1 + ]

append दो कोटेशन जोड़ता है, concat कोटेशनों के ऐरे को समतल करता है, map डेटा को कोटेशन में बदलता है, और >quotation नतीजे को कॉल करने लायक बनाता है। MACRO: बिल्कुल यही कौशल लेता है और उसे कंपाइल समय पर ले जाता है।

मैक्रो क्या है

MACRO: की परिभाषा दिखने में एक आम वर्ड जैसी होती है, लेकिन उसकी बॉडी आपके प्रोग्राम के कंपाइल होते समय चलती है, उसके चलने के समय नहीं। बॉडी को स्टैक पर एक कोटेशन छोड़ना होता है। फिर कंपाइलर उस कोटेशन को कॉल साइट में जोड़ देता है, यानी वह मैक्रो कॉल की जगह वह कोड रख देता है जो बॉडी ने बनाया था। रनटाइम पर कोई मैक्रो बचता ही नहीं, सिर्फ जनरेट किया गया कोड बचता है।

USING: kernel macros sequences ;

MACRO: twice ( quot -- spliced )
    dup append ;

जहाँ भी आप [ 1 + ] twice लिखते हैं, कंपाइलर:

  1. स्टैक पर [ 1 + ] रखकर बॉडी चलाता है,
  2. dup append कोटेशन [ 1 + 1 + ] बनाता है,
  3. उसका कोड, यानी 1 + 1 +, उसी जगह जोड़ देता है।

तो जो वर्ड twice को कॉल करता है, वह ऐसे कंपाइल होता है मानो आपने 1 + 1 + हाथ से लिखा हो:

: inc-twice ( x -- y ) [ 1 + ] twice ;

5 inc-twice .   ! => 7

मुख्य अंतर यह है: एक आम वर्ड जो कोटेशन लेता है, उसे रनटाइम पर call करता है। मैक्रो कभी कुछ कॉल नहीं करता, वह कंपाइलर को नया कोड सौंपता है ताकि कंपाइलर उसे उसकी जगह कंपाइल कर दे।

इनपुट लिटरल होने चाहिए

चूँकि बॉडी कंपाइल समय पर चलती है, मैक्रो के इनपुट उसी समय पता होने चाहिए। कॉल साइट पर वे लिटरल होने चाहिए, यानी एक [ … ] कोटेशन, एक संख्या, एक ऐरे या एक स्ट्रिंग। जो वैल्यू सिर्फ रनटाइम पर मौजूद होती है, वह मैक्रो को नहीं सौंपी जा सकती।

! DOCTEST: SKIP — illustrative call sites, not standalone code
[ 1 + ] twice          ! ok: the quotation is a literal
[ 1 + ] 3 repeat-quot  ! ok: the quotation and the number are literals

टूलकिट

मैक्रो की बॉडी आम कोड होती है जो एक कोटेशन लौटाती है। जिन वर्ड से आप पहले मिल चुके हैं, वही ज़्यादातर काम कर देते हैं:

बनाने के लिए… इस्तेमाल
[ A ] और [ B ] से [ A B ] append
कोटेशनों के ऐरे से एक कोटेशन concat >quotation
सीक्वेंस के हर एलिमेंट के लिए एक कोटेशन पहले map, फिर concat >quotation

sequences और kernel से आए दो और वर्ड इस किट को पूरा करते हैं।

<repetition> (sequences में) एक ही एलिमेंट की n प्रतियों का सीक्वेंस बनाता है। इसका स्टैक इफेक्ट ( length elt -- repetition ) है, यानी गिनती पहले आती है। नतीजा एक वर्चुअल सीक्वेंस होता है, इसलिए उसे आम तरीके से समतल कीजिए:

3 [ 1 + ] <repetition> concat >quotation .   ! => [ 1 + 1 + 1 + ]

curry (kernel में) एक कोटेशन के आगे एक वैल्यू लगा देता है, जिससे एक नया कोटेशन बनता है जो पहले उस वैल्यू को स्टैक पर डालता है:

3 [ + ] curry .          ! => [ 3 + ]
{ 1 2 3 } [ . ] curry .  ! => [ { 1 2 3 } . ]

curry से ही आप एक खास एलिमेंट को कोटेशन में पक्का कर देते हैं, और यही { 10 20 30 } और [ + ] को [ 10 + ] [ 20 + ] [ 30 + ] में बदल देता है।

यह सुनने में जितना मुश्किल लगता है, उससे आसान क्यों है

कुछ भाषाओं में मैक्रो के लिए खास क्वोट/अनक्वोट सिंटैक्स और नामों के टकराव से बचने के लिए कुछ अतिरिक्त चीज़ें चाहिए होती हैं। Factor को इसमें से कुछ नहीं चाहिए: एक कोटेशन पहले ही हल हो चुके वर्ड का सीक्वेंस होता है, क्योंकि [ … ] पढ़ते समय पार्सर ने हर dup, + और append को ढूँढ लिया था। इसलिए मैक्रो की बॉडी बस वही सीक्वेंस के साथ की जाने वाली छेड़छाड़ है जो आप पहले से करते हैं, बस उससे एक कोटेशन लौट आता है।

करीबी रिश्तेदार: $[ … ]

मैक्रो एक कोटेशन लौटाता है ताकि उसे किसी कॉल में जोड़ा जा सके। लेकिन जब आप पार्स समय पर गिनी जाने वाली कोई सादी वैल्यू चाहते हैं, तब $[ code ] फाइल पढ़े जाने के दौरान code चलाता है और नतीजे को लिटरल बनाकर सोर्स में मिला देता है:

! DOCTEST: SKIP — compile-time value, shown for contrast
USING: calendar literals ;

CONSTANT: build-time $[ now ]

$[ … ] literals वोकैबुलरी से आता है, जो पार्स समय के नतीजों को लिटरल में शामिल करने वाले वर्ड इकट्ठा करती है। इसे सिर्फ पूर्णता के लिए बताया गया है। आप जो चार स्टेंसिल लिखेंगे, यानी twice, repeat-quot, compose-many और each-literal, वे सब MACRO: घोषणाएँ हैं।

निर्देश

आप एक सिग्नल स्टेशन की कोड जनरेशन टूलकिट का रखरखाव कर रहे हैं। हर मैक्रो एक स्टेंसिल है, यानी एक छोटा टेम्पलेट जिसे कंपाइलर हर कॉल साइट पर विशेष कोड बनाने के लिए इस्तेमाल करता है।

आपको सिर्फ चार मैक्रो लिखने हैं। टेस्ट फाइल छोटे रैपर शब्द बनाती है जो आपके मैक्रो को कॉल करते हैं (मैक्रो को उसके द्वारा बनाए गए कोड के जरिए ही देखा जा सकता है), और फिर उन रैपर के रनटाइम परिणाम जाँचती है। रैपर को वैसे ही रहने दीजिए।

हर टास्क में बॉडी को स्टैक पर कॉल साइट के लिटरल इनपुट मिलते हैं, और उसे एक कोटेशन छोड़ना होता है। "कंपाइल होकर यह बनेगा" वाली लाइनें बताती हैं कि आपके कोटेशन से कौन-सा सीधा कोड बनना चाहिए।

1. दो बार

twice एक लिटरल कोटेशन लेता है और एक ऐसा कोटेशन लौटाता है जो मूल कोटेशन को लगातार दो बार चलाता है।

! : inc-twice ( x -- y ) [ 1 + ] twice ;
! body receives [ 1 + ]  ->  must build [ 1 + 1 + ]
5 inc-twice .   ! => 7

2. N बार दोहराव

repeat-quot एक लिटरल कोटेशन और एक लिटरल गिनती n लेता है, और एक ऐसा कोटेशन लौटाता है जो मूल कोटेशन को लगातार n बार चलाता है। n = 0 होने पर खाली कोटेशन [ ] लौटता है।

! : inc-thrice ( x -- y ) [ 1 + ] 3 repeat-quot ;
! body receives [ 1 + ] 3  ->  must build [ 1 + 1 + 1 + ]
5 inc-thrice .   ! => 8

3. कई कोटेशन जोड़ना

compose-many कोटेशन का एक लिटरल ऐरे लेता है और एक ही कोटेशन लौटाता है जो हर एक को बारी-बारी चलाता है। खाली ऐरे खाली कोटेशन लौटाता है।

! : pipeline ( x -- y ) { [ 1 + ] [ 2 * ] [ 1 - ] } compose-many ;
! body receives { [ 1 + ] [ 2 * ] [ 1 - ] }  ->  must build [ 1 + 2 * 1 - ]
5 pipeline .   ! => 11

4. अनरोल्ड इटरेशन

each-literal एक लिटरल सीक्वेंस और एक कोटेशन लेता है। यह एक ऐसा कोटेशन लौटाता है जो सीक्वेंस के हर एलिमेंट के लिए पहले उस एलिमेंट को स्टैक पर रखता है और फिर कोटेशन चलाता है। यह इटरेशन कंपाइल समय पर ही होता है, इसलिए बनने वाला कोड सीधा होता है।

! : sum-three ( -- n ) 0 { 10 20 30 } [ + ] each-literal ;
! body receives { 10 20 30 } [ + ]  ->  must build [ 10 + 20 + 30 + ]
sum-three .   ! => 60

चारों में यह सबसे ज़्यादा संरचना पर निर्भर है: बनने वाले कोड को सीक्वेंस की लंबाई और उसके एलिमेंट की वैल्यू, दोनों आकार देते हैं। संकेतों में इसे एक-एक कदम करके बनाने का तरीका बताया गया है।

GitHub के ज़रिए संपादित करें यह लिंक एक नई विंडो या टैब में खुलता है
Factor Exercism

संकेत स्टेंसिल शुरू करने के लिए तैयार हैं?

Exercism पर साइन अप कीजिए और Factor को 47 कॉन्सेप्ट163 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।