ট্র্যাক
/
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 + ] তৈরি হয়।

এটা শুনতে যতটা কঠিন মনে হয়, আসলে ততটা নয়

কিছু ভাষায় ম্যাক্রোর জন্য বিশেষ quote/unquote সিনট্যাক্স লাগে, আর নামের সংঘর্ষ এড়াতে বাড়তি কাঠামোও দরকার হয়। 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: ডিক্লারেশন।

নির্দেশনা

আপনি একটি সিগন্যাল স্টেশনের কোড-জেনারেশন টুলকিট রক্ষণাবেক্ষণ করছেন। প্রতিটি ম্যাক্রো হলো একটি স্টেনসিল, অর্থাৎ একটি ছোট টেমপ্লেট, যা কম্পাইলার প্রতিটি কল সাইটে বিশেষায়িত কোড তৈরি করতে ব্যবহার করে।

আপনি শুধু চারটি ম্যাক্রো লেখেন। টেস্ট ফাইলটি ছোট কিছু র্যাপার শব্দ ডিফাইন করে, যেগুলো আপনার ম্যাক্রোগুলোকে কল করে (একটি ম্যাক্রোকে কেবল তার জেনারেট করা কোড দিয়েই দেখা যায়), তারপর র্যাপারগুলোর রানটাইম ফলাফল পরীক্ষা করে। র্যাপারগুলো যেমন আছে তেমনই রাখুন।

প্রতিটি কাজে, বডি স্ট্যাকে কল সাইটের লিটারাল ইনপুটগুলো পায় এবং একটি কোয়োটেশন রেখে যেতে হবে। ‘compiles to’ লাইনগুলো দেখায় আপনার কোয়োটেশন থেকে যে স্ট্রেইট-লাইন কোড তৈরি হওয়া উচিত।

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টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।