در Clojure، لیستها یک مجموعه هستند، دقیقاً مانند لیستها در زبانهای دیگر. مانند دیگر زبانهای خانوادهی لیسپ، Clojure برای بیان لیستها از پرانتز استفاده میکند.
لیستهای Clojure به یکی از دو روش ساخته میشوند. تابع list میتواند یک لیست بسازد، یا میتوانید یک لیست تحتاللفظی را quote کنید.
لیستها ویژگی خاصی دارند، چون Clojure با آنها مانند یک فراخوانی رفتار میکند. انتظار دارد که فراخوانی با یک عملگر شروع شود که معمولاً یک تابع است. عنصرهای باقیماندهی لیست عملوند در نظر گرفته میشوند، یعنی به آرگومانهای تابع تبدیل میشوند.
همین رفتار ویژهی Clojure با لیستها سبب میشود نتوانیم یک لیست تحتاللفظی را مستقیم بسازیم. quote کردن یک لیست، یا استفاده از شکل کوتاهشدهی آن '، نشان میدهد که آن لیست نباید ارزیابی شود.
برخلاف برخی زبانهای مدرن، لیستهای Clojure ناهمگون هستند، یعنی میتوانند درون خود چند نوع مختلف عنصر داشته باشند، مثلاً '(2 "a" "b" 3).
برخلاف دیگر لیسپها، یک لیست خالی در Clojure بهعنوان «درست» در نظر گرفته میشود و معادل nil یا false نیست.
الیس برای سفر پیشرویش به وگاس خیلی منتظر است تا کمی پوکر (و بازیهای کارتی دیگر) بازی کند. او که طرفدار سرسخت «خودردیابی» است، میخواهد چند تابع کوچک بنویسد که در کارهای ردیابی به او کمک کنند و از شما خواسته تا در فکر کردن به آنها یاریاش کنید.
الیس بهطور ویژه پوکر را دوست دارد و میخواهد پیگیری کند که چند راند بازی میکند و کدام راندها هستند. هر راند شمارهی خودش را دارد و هر میز شمارهی راندی را که در حال بازی است نشان میدهد. الیس یک میز انتخاب میکند و مینشیند تا اولین راندش را بازی کند. او قصد دارد سه راند بازی کند.
تابعی به اسم rounds پیادهسازی کنید که شمارهی راند فعلی را میگیرد و یک list برمیگرداند که شامل همان راند و دو راند بعدی است:
(rounds 27)
;;=> (27 28 29)
الیس چند راند در میز اول بازی کرد، بعد استراحت کرد و چند راند دیگر در میز دوم بازی کرد ... اما در نهایت برای هر میز یک لیست جداگانه داشت! او میخواهد آن دو لیست را با هم یکی کند تا بتواند همهی راندهای پوکر را در یک جا پیگیری کند.
تابعی به اسم concat-rounds پیادهسازی کنید که دو لیست میگیرد و یک list برمیگرداند که شامل همهی راندهای list اول و سپس همهی راندهای list دوم است:
(concat-rounds '(27 28 29) '(35 36))
;;=> (27 28 29 35 36)
هنگام صحبت دربارهی برخی از راندهای قبلی پوکر، بازیکن دیگری اشاره میکند که دو تای آنها چقدر شبیه هم پیش رفتند. الیس مطمئن نیست که آن راندها را بازی کرده یا نه.
تابعی به اسم contains-round? پیادهسازی کنید که دو آرگومان میگیرد: لیستی از راندهای بازیشده و یک شمارهی راند. تابع اگر آن راند در لیست راندهای بازیشده باشد true و در غیر این صورت false برمیگرداند:
(contains-round? '(27 28 29 35 36) 29)
;;=> true
(contains-round? '(27 28 29 35 36) 30)
;;=> false
الیس میخواهد بازی جدیدی به نام Black Joe را امتحان کند. این بازی شبیه Black Jack است، جایی که هدفتان این است که کارتهای دستتان به یک مقدار هدف برسند؛ اما در Black Joe هدف این است که میانگین مقادیر کارتها ۷ شود. میانگین را میتوان با جمع زدن همهی مقادیر کارتها و سپس تقسیم آن مجموع بر تعداد کارتهای دست به دست آورد.
تابعی به اسم card-average پیادهسازی کنید که مقدار میانگین یک دست Black Joe را برمیگرداند.
(card-average '(5 6 7))
;;=> 6.0
در Black Joe سرعت مهم است. الیس میخواهد راه سریعتری برای یافتن میانگین پیدا کند.
او دو راه برای رسیدن به عددی شبیه به میانگین به ذهنش رسیده است:
تابع approx-average? را پیادهسازی کنید که hand را میگیرد، یعنی لیستی شامل مقادیر کارتهای دستتان.
اگر یکی or هر دو از این راهبردهای نامبردهشده در بالا به عددی برابر با میانگین واقعی برسند، true برگردانید.
نکته: طول همهی دستها فرد است تا یافتن میانه آسانتر شود.
(approx-average? '(1 2 3))
;;=> true
(approx-average? '(2 3 4 8 8))
;;=> true
(approx-average? '(1 2 3 5 9))
;;=> false
الیس که از نتایج آزمایش میانگینگیریاش کنجکاو شده، میخواهد بداند آیا میانگین کارتهای در جایگاههای زوج در برابر میانگین کارتهای در جایگاههای فرد نتیجهی یکسانی میدهد یا نه. وقت یک تابع آزمایشی دیگر است!
تابعی به اسم average-even-odd? پیادهسازی کنید که یک «مقدار منطقی» برمیگرداند که نشان میدهد آیا میانگین کارتهای در اندیسهای زوج با میانگین کارتهای در اندیسهای فرد یکسان است یا نه.
(average-even-odd? '(1 2 3))
;;=> true
(average-even-odd? '(1 2 3 4))
;;=> false
در Black Joe هر یازدهمین دست، یک دست امتیازی با یک قانون امتیازی است: اگر آخرین کارتی که میکشید یک سرباز باشد، مقدارش را دو برابر میکنید.
تابعی به اسم maybe-double-last پیادهسازی کنید که یک دست میگیرد و بررسی میکند آیا آخرین کارت یک سرباز (۱۱) است. اگر آخرین کارت یک سرباز (۱۱) باشد، پیش از برگرداندن دست، مقدارش را دو برابر کنید.
(maybe-double-last '(5 9 11))
;;=> '(5 9 22)
(maybe-double-last '(5 9 10))
;;=> '(5 9 10)