অনেক লিস্পার আপনাকে বলে দেবে যে লিস্পকে বিশেষ করে তোলে তা হমাইকোনসিটিটি , যার অর্থ কোডের বাক্য গঠনটি অন্য ডেটার মতো একই ডেটা স্ট্রাকচার ব্যবহার করে প্রতিনিধিত্ব করা হয়। উদাহরণস্বরূপ, প্রদত্ত পাশের দৈর্ঘ্যের সাথে ডান-কোণযুক্ত ত্রিভুজটির অনুমিতি গণনা করার জন্য এখানে একটি সাধারণ ফাংশন (স্কিম সিনট্যাক্স ব্যবহার করে):
(define (hypot x y)
(sqrt (+ (square x) (square y))))
এখন, হোমোকোনসিটিটি বলে যে উপরের কোডটি লিস্প কোডে একটি ডেটা স্ট্রাকচার (বিশেষত তালিকার তালিকাগুলি) হিসাবে উপস্থাপনযোগ্য। সুতরাং, নিম্নলিখিত তালিকা বিবেচনা করুন এবং দেখুন তারা কীভাবে একসাথে "আঠালো":
(define #2# #3#)
(hypot x y)
(sqrt #4#)
(+ #5# #6#)
(square x)
(square y)
ম্যাক্রোস আপনাকে উত্স কোডটি ঠিক তেমন আচরণ করার অনুমতি দেয়: স্টাফের তালিকা। সেই 6 "sublists" এর একে অপরের তালিকাতে পারেন পয়েন্টার থাকে, বা প্রতীক (এই উদাহরণে: define
, hypot
, x
, y
, sqrt
, +
, square
)।
সুতরাং, আমরা কীভাবে হোমকোনসিটিটি সিনট্যাক্সকে "আলাদা" করতে এবং ম্যাক্রো তৈরি করতে পারি? এখানে একটি সহজ উদাহরণ। আসুন let
ম্যাক্রোটিকে পুনরায় প্রতিস্থাপন করুন , যা আমরা কল করব my-let
। একটি অনুস্মারক হিসেবে,
(my-let ((foo 1)
(bar 2))
(+ foo bar))
মধ্যে প্রসারিত করা উচিত
((lambda (foo bar)
(+ foo bar))
1 2)
এখানে স্কিম "স্পষ্ট পুনঃনামকরনের" ম্যাক্রো ব্যবহার করে একটি বাস্তবায়ন এর † :
(define-syntax my-let
(er-macro-transformer
(lambda (form rename compare)
(define bindings (cadr form))
(define body (cddr form))
`((,(rename 'lambda) ,(map car bindings)
,@body)
,@(map cadr bindings)))))
form
পরামিতি প্রকৃত ফর্ম আবদ্ধ হয়, তাই আমাদের উদাহরণস্বরূপ, এটি হবে (my-let ((foo 1) (bar 2)) (+ foo bar))
। সুতরাং, আসুন উদাহরণের মাধ্যমে কাজ করা যাক:
- প্রথমে, আমরা ফর্মটি থেকে বাইন্ডিংগুলি পুনরুদ্ধার করি। ফর্ম অংশ
cadr
ধরে ((foo 1) (bar 2))
।
তারপরে, আমরা ফর্মটি থেকে দেহটি পুনরুদ্ধার করি। ফর্ম অংশ cddr
ধরে ((+ foo bar))
। (নোট করুন যে এটি বাইন্ডিংয়ের পরে সমস্ত সাবফর্মগুলি দখল করার উদ্দেশ্যে; তাই যদি ফর্মটি থাকে
(my-let ((foo 1)
(bar 2))
(debug foo)
(debug bar)
(+ foo bar))
তাহলে শরীর হবে ((debug foo) (debug bar) (+ foo bar))
।)
- এখন, আমরা প্রকৃতপক্ষে ফলাফলটি
lambda
প্রকাশ করি এবং আমাদের সংগ্রহ করা বাইন্ডিং এবং বডি ব্যবহার করে কল করি। ব্যাকটিক একটি "quasiquote", যা আক্ষরিক datums যেমন quasiquote ভিতরে আচরণ সবকিছু মানে বলা হয়, ব্যতীত কমা ( "উদ্ধতি") পর বিট।
(rename 'lambda)
কোনো পদ্ধতি ব্যবহার করার জন্য lambda
এই ম্যাক্রো হয় বহাল বাঁধাই সংজ্ঞায়িত , বরং যাই হোক না কেন চেয়ে lambda
বাঁধাই কাছাকাছি হতে পারে এই ম্যাক্রো হয় ব্যবহৃত । (এটি স্বাস্থ্যবিধি হিসাবে পরিচিত ))
(map car bindings)
রিটার্ন দেয় (foo bar)
: প্রতিটি বাইন্ডিংয়ের প্রথম ডেটাম।
(map cadr bindings)
রিটার্ন দেয় (1 2)
: প্রতিটি বাইন্ডিংয়ের মধ্যে দ্বিতীয় ডাটাম।
,@
"স্প্লাইকিং" করে, যা একটি তালিকা ফেরত দেওয়ার মত প্রকাশের জন্য ব্যবহৃত হয়: এটি তালিকার উপাদানগুলিকে তালিকার পরিবর্তে ফলাফলের মধ্যে আটকে দেয়।
- এই সমস্ত একসাথে রেখে, আমরা ফলস্বরূপ, তালিকাটি পাই
(($lambda (foo bar) (+ foo bar)) 1 2)
, যেখানে $lambda
এখানে নাম পরিবর্তন করা হয়েছে lambda
।
সোজা, ঠিক? ;-) (এটি যদি আপনার পক্ষে সোজা না হয় তবে অন্য ভাষার জন্য ম্যাক্রো সিস্টেমটি প্রয়োগ করা কতটা কঠিন হবে তা ভেবে দেখুন))
সুতরাং, আপনার কাছে অন্যান্য ভাষার জন্য ম্যাক্রো সিস্টেম থাকতে পারে, যদি আপনার কাছে একটি উপায় থাকে তবে নন-ক্লঙ্কি উপায়ে সোর্স কোডটিকে "আলাদা" করতে সক্ষম হবেন। এ নিয়ে কিছু চেষ্টা আছে। উদাহরণস্বরূপ, sweet.js জাভাস্ক্রিপ্টের জন্য এটি করে।
Ed পঠিত স্কিমারগুলি এটি পড়ার জন্য, আমি ইচ্ছাকৃতভাবে defmacro
অন্যান্য লিস্প উপভাষার দ্বারা ব্যবহৃত মধ্যস্থতা হিসাবে সুস্পষ্ট নামকরণ ম্যাক্রোগুলিকে বেছে নেওয়া পছন্দ করেছি এবং syntax-rules
(যা স্কিমে এই জাতীয় ম্যাক্রো কার্যকর করার মানক উপায় হবে)। আমি অন্য লিস্প উপভাষায় লিখতে চাই না, তবে আমি অভ্যস্ত নন-স্কিমারদের আলাদা করতে চাই না syntax-rules
।
রেফারেন্সের জন্য, my-let
ম্যাক্রো যা ব্যবহার করে তা এখানে syntax-rules
:
(define-syntax my-let
(syntax-rules ()
((my-let ((id val) ...)
body ...)
((lambda (id ...)
body ...)
val ...))))
সম্পর্কিত syntax-case
সংস্করণটি দেখতে খুব অনুরূপ:
(define-syntax my-let
(lambda (stx)
(syntax-case stx ()
((_ ((id val) ...)
body ...)
#'((lambda (id ...)
body ...)
val ...)))))
দুই এর মধ্যে পার্থক্য যে সবকিছু মধ্যে syntax-rules
একটি অন্তর্নিহিত রয়েছে #'
, ফলিত যাতে আপনি করতে পারেন শুধুমাত্র মধ্যে প্যাটার্ন / টেমপ্লেট জোড়া আছে syntax-rules
, অত এটা সম্পূর্ণরূপে ঘোষণামূলক আছে। বিপরীতে, এর মধ্যে syntax-case
, প্যাটার্নের পরে বিটটি আসল কোড যা শেষ পর্যন্ত একটি সিনট্যাক্স অবজেক্ট ( #'(...)
) ফিরিয়ে দিতে হবে তবে অন্যান্য কোডও এতে থাকতে পারে।