Common Lisp-এ একটি ফাংশনের আর্গুমেন্ট লিস্টকে বলা হয় 'ল্যাম্বডা লিস্ট'।
একটি ল্যাম্বডা লিস্টে বিভিন্ন টাইপের আর্গুমেন্ট থাকতে পারে।
এই বিভিন্ন টাইপকে চিহ্নিত করা হয় 'ল্যাম্বডা লিস্ট কিওয়ার্ড' দিয়ে, যেগুলো সবই & দিয়ে শুরু হয়।
সবচেয়ে বেশি ব্যবহৃত টাইপগুলো হলো অপশনাল, কিওয়ার্ড আর রেস্ট আর্গুমেন্ট টাইপ।
কোনো একটি নির্দিষ্ট ল্যাম্বডা লিস্ট কিওয়ার্ডের পরের প্রতিটি প্যারামিটারই ওই টাইপের হবে।
একটি ল্যাম্বডা লিস্টে একটি ল্যাম্বডা লিস্ট কিওয়ার্ড কেবল একবারই ব্যবহার করা যায়।
ল্যাম্বডা লিস্ট ব্যবহৃত হয় আরও কিছু কনস্ট্রাক্টেও, যেগুলো নিয়ে পরে আলোচনা করা হবে, যেমন ডিস্ট্রাকচারিং আর ম্যাক্রো।
Common Lisp-এ একটি ফাংশনের কিছু আর্গুমেন্ট অপশনাল হতে পারে।
ল্যাম্বডা লিস্টে এগুলো &optional ল্যাম্বডা লিস্ট কিওয়ার্ড দিয়ে চিহ্নিত করা হয়।
কোনো প্যারামিটার নির্দিষ্ট করা না হলে সেটি nil মানে বাইন্ড হবে।
একাধিক অপশনাল প্যারামিটার থাকলে সেগুলো ক্রমানুসারে বাইন্ড হয়।
অপশনাল প্যারামিটারের জন্য ডিফল্ট মান নির্দিষ্ট করা যায়।
সবশেষে, প্রতিটি অপশনাল প্যারামিটারের জন্য একটি সিম্বল নির্দিষ্ট করা যায়, যেটি ট্রু বা ফলস-এ বাইন্ড হবে নির্ভর করে ফাংশনের কলকারী ওই প্যারামিটারটি সরবরাহ করেছে কি না তার ওপর (এটাকে বলা হয় "supplied-p parameter")।
(defun optional-parameters (&optional x (y 'default) (z nil z-supplied-p))
(list x y (if z-supplied-p (list :z-was-supplied z)
(list :z-was-not-supplied z))))
(optional-parameters) ; => (NIL DEFAULT (:Z-WAS-NOT-SUPPLIED NIL))
(optional-parameters 5 nil 10) ; => (5 NIL (:Z-WAS-SUPPLIED 10))
Common Lisp-এ একটি ফাংশনের নামযুক্ত প্যারামিটার থাকতে পারে (এগুলোকে বলা হয় "কিওয়ার্ড প্যারামিটার")।
ল্যাম্বডা লিস্টে এগুলো &key ল্যাম্বডা লিস্ট কিওয়ার্ড দিয়ে চিহ্নিত করা হয়।
কিওয়ার্ড প্যারামিটার আবশ্যক প্যারামিটার নয়।
অপশনাল প্যারামিটারের মতোই এগুলোকে ডিফল্ট মান দেওয়া যায়, আর সেগুলো সরবরাহ করা হয়েছে কি না তা বোঝানোর জন্য সিম্বল বাইন্ড করা যায়।
কিওয়ার্ড প্যারামিটারওয়ালা কোনো ফাংশন কল করার সময় প্যারামিটারের মানের সামনে কিওয়ার্ড হিসেবে প্যারামিটারটির নাম বসানো হয়। ফাংশনের কলকারী যেকোনো ক্রমে কিওয়ার্ড প্যারামিটার নির্দিষ্ট করতে পারেন।
(defun keyword-parameters (&key x (y 'default) (z nil z-supplied-p))
(list x y (if z-supplied-p (list :z-was-supplied z)
(list :z-was-not-supplied z))))
(keyword-parameters) ; => (NIL DEFAULT (:Z-WAS-NOT-SUPPLIED NIL))
(keyword-parameters :y 5) ; => (NIL 5 (:Z-WAS-NOT-SUPPLIED NIL))
(keyword-parameters :z 10 :x 5) ; => (5 DEFAULT (:Z-WAS-SUPPLIED 10))
অপশনাল আর কিওয়ার্ড প্যারামিটার একসাথে ব্যবহার করার সময় সাবধান থাকা উচিত, কারণ কিওয়ার্ডের নাম আর আর্গুমেন্ট অপশনাল প্যারামিটার খেয়ে ফেলতে পারে:
(defun could-be-confusing (&optional x y &key z) (list x y z))
(could-be-confusing :z 'huh?) ; => (:Z HUH? NIL)
Common Lisp-এ একটি ফাংশনের এমন একটি প্যারামিটার থাকতে পারে, যেটিতে আবশ্যক বা অপশনাল প্যারামিটার প্রসেস হওয়ার পর আর্গুমেন্টগুলোর "বাকি অংশ" থাকবে।
এই প্যারামিটারটি &rest ল্যাম্বডা লিস্ট কিওয়ার্ড দিয়ে চিহ্নিত করা হয়।
ফাংশনের সব আর্গুমেন্ট যদি অন্য টাইপের প্যারামিটার ব্যবহার করে ফেলে, তাহলে রেস্ট প্যারামিটারটি একটি খালি লিস্টে বাইন্ড হবে।
অব্যবহৃত আর্গুমেন্ট থাকলে রেস্ট প্যারামিটারটি সেই আর্গুমেন্টগুলোর একটি লিস্টে বাইন্ড হবে।
(defun rest-of-it (req &optional opt &rest rest) (list req opt rest))
(rest-of-it 1) ; => (1 NIL NIL)
(rest-of-it 1 2) ; => (1 2 NIL)
(rest-of-it 1 2 3) ; => (1 2 (3))
(rest-of-it 1 2 3 4 5) ; => (1 2 (3 4 5))
লিলি নামের লিস্প অ্যালিয়েন আপনার সাহায্যে নিশ্চিতভাবেই অনেক লাসাগনা বানিয়েছে। কৃতজ্ঞতা জানাতে সে আপনাকে কিছু উদ্বৃত্ত খাবার দিতে চায়। তবে তার আগে, আপনি যে ফাংশনগুলোর কাজে সাহায্য করেছিলেন তার একটির ব্যাপারে তার কিছু মতামত আছে।
এই অনুশীলনীতে আপনি আরও কিছু ফাংশন লিখবেন, যা দিয়ে লিলিকে তার লাসাগনা রান্নায় সাহায্য করবেন, আর আপনার সাথে উদ্বৃত্ত খাবার ভাগ করে নিতেও সাহায্য করবেন।
এখানে আপনার জন্য ছয়টি কাজ আছে: তিনটি আগের লাসাগনা রান্নার ফাংশনগুলোর উন্নতি করা নিয়ে, আর তিনটি উদ্বৃত্ত খাবার ভাগ করা নিয়ে।
preparation-time-in-minutes ব্যবহার করা আরও সহজ করুনলিলি বলছে, আপনার preparation-time-in-minutes ব্যবহার করা তার কাছে একটু অস্বস্তিকর লাগছিল, কারণ ফাংশনগুলো ব্যবহার করার আগে তাকে সব স্তর গুনতে হতো। লিলি ভাবছে, আপনি কি ফাংশনটির এমন একটি সংস্করণ বানাতে পারবেন যা আর্গুমেন্ট হিসেবে স্তরগুলো (যতগুলোই হোক) নেয়, আর তারপর কতগুলো স্তর আছে তার ভিত্তিতে প্রস্তুতির সময় হিসাব করে? মনে করিয়ে দিই, একটি স্তর প্রস্তুত করতে ১৯ মিনিট লাগে।
(preparation-time 'sauce 'cheese 'right-handed-macaroni 'cheese 'sauce
'left-handed-macaroni 'sauce 'sauce 'cheese 'cheese)
;; => 190 (because there are 10 layers.)
লিলির প্যারেন্টাল-ইউনিটরা সবসময় তাদের লাসাগনা ৩৩৭ মিনিট ধরে রান্না করত, তবুও লিলি স্বীকার করে যে অন্য লিস্প অ্যালিয়েনরা তাদের লাসাগনা আরও বেশি বা কম সময় ধরে রান্না করতে পছন্দ করে।
তাই ভালো হবে যদি remaining-minutes-in-oven আর expected-minutes-in-oven ব্যবহার না করে, বরং একটি ঐচ্ছিক প্যারামিটার নিতে পারে যা ঠিক করে সময়টা বেশি, খুব বেশি, কম নাকি খুব কম হবে। বেশি বা কম মানে ডিফল্ট ৩৩৭ সময়ের সাথে ১০০ যোগ বা বিয়োগ করা। খুব বেশি বা খুব কম মানে ২০০ যোগ বা বিয়োগ করা। কোনো প্যারামিটার দেওয়া না থাকলে, বা আর্গুমেন্টটি 'normal' হলে, ডিফল্ট মান ৩৩৭ রিটার্ন করা উচিত।
(remaining-minutes-in-oven) ;; => 337
(remaining-minutes-in-oven :normal) ;; => 337
(remaining-minutes-in-oven :shorter) ;; => 237
(remaining-minutes-in-oven :very-short) ;; => 137
(remaining-minutes-in-oven :longer) ;; => 437
(remaining-minutes-in-oven :very-long) ;; => 537
সেই পরিবর্তন শেষ করার পরপরই লিলির মনে পড়ল যে কিছু লিস্প অ্যালিয়েন আছে যারা একেবারে কম রান্নার সময় পছন্দ করে। আসলে তারা চায় এটা কাঁচা থাকুক। তাই এখন ফাংশনটি যদি আর্গুমেন্ট হিসেবে NIL পায় তাহলে 0 রিটার্ন করবে, আর আগের কাজের মতোই বাকি মানগুলোও দেবে।
(remaining-minutes-in-oven) ;; => 337
(remaining-minutes-in-oven nil) ;; => 0
এখন যেহেতু আপনি লাসাগনা রান্নার ফাংশনগুলোর উন্নতিতে সাহায্য করেছেন, অনেক লাসাগনা জমে গেছে! লিলি আপনার সব সাহায্যের কৃতজ্ঞতা হিসেবে আপনার সাথে উদ্বৃত্ত খাবার ভাগ করে নিতে চায়। আপনাকে একটি ফাংশন লিখতে হবে যা তিনটি প্যারামিটার নেয়: মোট উদ্বৃত্ত খাবার, লিলি তার আলমারিতে যতগুলো খাবারের পাত্র পেয়েছে, আর আপনার জন্য একই সংখ্যা। যেহেতু খাবারের পাত্র খুঁজতে ভিন্ন ভিন্ন সময় লাগতে পারে, আর আপনি ও লিলি আলমারি খুঁজতে ব্যস্ত থাকার সময়েই উদ্বৃত্ত খাবারের সঠিক পরিমাণ ঠিক হয়ে যেতে পারে, তাই你们两个 একমত যে ফাংশনটি আর্গুমেন্ট যেকোনো ক্রমে নেবে, যাতে প্রতিটি সংখ্যা ঠিক হওয়ার সাথে সাথেই আপনি সেটি লিখে রাখতে পারেন। তিনটি সংখ্যাই পেয়ে গেলে ফাংশন কলের বন্ধনীটি বন্ধ করে সেটি ইভালুয়েট করতে পারেন।
ফাংশনটি এমন উদ্বৃত্ত খাবারের পরিমাণ রিটার্ন করবে যা সত্যিই উদ্বৃত্ত থেকে যায়, অর্থাৎ উদ্বৃত্ত খাবারের ওজন আর আপনি ও লিলি মিলে যতগুলো পাত্র পেয়েছেন সেই সংখ্যার পার্থক্য।
(split-leftovers :weight 20 :human 10 :alien 5) ;; => 5
(split-leftovers :weight 20 :alien 10 :human 2) ;; => 8
(split-leftovers :alien 12 :weight 20 :human 4) ;; => 4
নিজ নিজ আলমারিতে কয়েকবার যাওয়া-আসার পর আপনি আর লিলি দুজনেই একমত হলেন যে, সংখ্যা আলাদা করে না বললে ফাংশনটি যদি ধরে নিতে পারত যে আপনার বা লিলির কাছে... ধরা যাক, ১০টি পাত্র আছে, তাহলে দারুণ হতো।
(split-leftovers :weight 20 :human 5) ;; => 5
(split-leftovers :weight 20 :alien 5) ;; => 5
(split-leftovers :weight 20) ;; => 0
আরও ভালো হতো যদি ফাংশনটি ধরে নিতে পারত যে পাত্রগুলো ভরে যাওয়ার মতো যথেষ্ট উদ্বৃত্ত খাবার আছে। তাই ওজন দেওয়া না থাকলে ফাংশনটি :just-split-it রিটার্ন করবে। তবে ওজন দেওয়া থাকলে আর সেটি nil হলে, ফাংশনটি :looks-like-someone-was-hungry রিটার্ন করবে।
(split-leftovers :human 5 :alien 5) ;; => :JUST-SPLIT-IT
(split-leftovers :weight NIL :human 5 :alien 5) ;; => :LOOKS-LIKE-SOMEONE-WAS-HUNGRY
Exercism-এ সাইন আপ করুন, Common Lisp ট্র্যাকের 27টি কনসেপ্ট92টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।