آموزش کلوژر برای برنامهنویسان جاوا
اخیرا کلوژر توجهها را به خودش جذب کردهاست. در ادامه با مثالهایی به تفاوت سینتکس این زبان و جاوا میپردازیم.
در دانشگاه، هیچ واحدی برای یادگیری لیسپ (یا هر زبان تابعی دیگری) نداشتم. بعد از دانشگاه به عنوان برنامهنویس جاوا مشغول به کار شدم و باز هم ارتباطی با برنامهنویسی تابعی نداشتم. بالاخره کدی مشابه کد زیر دیدم:
(defn square [x] (* x x)) (deftest square-test (is (= 0 (square 0))) (is (= 1 (square -1))) (is (= 1 (square 1))))
فکر کردم که استفاده از این زبان دیوانگی است! اما با محبوبیت بیشتر و بیشتر برنامهنویسی تابعی، تصمیم گرفتم به لیسپ شانسی برای یادگیری بدهم. در این مطلب، تلاش میکنم زیباییهای کلوژر (لیسپ برای ماشین مجازی جاوا) را به شما نشان بدهم.
توجه: من هنوز در حال یادگیری زبان هستم بنابراین ممکن است برداشتهایم ساده و دور از واقعیت باشد. اگر شما برنامهنویس خبرهٔ لیسپ یا کلوژر هستید، خوشحال میشوم با نظراتتان مطلب را بهبود دهید.
اولین برنامه
(println "Hello world!")
این برنامه Hello World در زبان کلوژر است که در REPL آن را اجرا میکنیم. (برای آشنایی با REPL این مطلبِ پیشتر منتشرشده در جاواکاپ را مطالعه کنید.)
سینتکس زبان بسیار ساده است:
- همه چیز داخل پرانتز قرار میگیرد.
(...)
- اسم تابع اول قرار میگیرد.
(function-name ...)
- آرگومانها (در صورت وجود) بعد از اسم تابع میآیند.
(function-name arguments)
تابع square
که در ابتدای مطلب تعریف کردیم، به صورت زیر استفاده میشود:
(square 2)
همانطور که میبینید، تعداد پرانتزها در کلوژر نسبت به جاوا تفاوتی ندارد بلکه تنها کافیست پرانتز را به سمت چپِ اسم تابع انتقال دهید!
Java: Clojure square(2); (square 2) square(square(2)); (square (square 2))
عملیاتهای ریاضی
عملیاتهای ریاضی مثل جمع و تفریق هم از قاعده ذکرشده استثنا نیستند و عملگر +
یا −
باید ابتدای عبارت بیاید. در واقع این نمادها، نماد تابعهای کلوژر هستند. ممکن است کمی زمان ببرد که به این شکل عادت کنید.
(+ 2 2) (- 2 2) (* 2 2) (/ 2 2)
باید دقت داشت که این نحوه نمایش، از نحوه نمایش عادی، سختتر نیست. فقط کافیست یادمان باشد که عملیات مربوطه همیشه ابتدا میآید.
Java: Clojure: 2 + 2 (+ 2 2) square(2); (square 2)
تعریف تابع
برای تعریف تابع از ماکروی defn
استفاده میکنیم.
۱. ابتدا defn را مینویسیم.
(defn ...)
۲. سپس اسم تابع را مشخص میکنیم.
(defn function-name ...)
۳. در ادامه، لیست پارامترهای ورودی را در براکت مینویسیم.
(defn function-name [param] ...)
۴. در نهایت بدنه تابع را میآوریم.
(defn function-name [param1 param2] expression1 expression2)
آخرین عبارت در بدنهٔ تابع، به عنوان دستور return عمل میکند.
Java: Clojure: int f() { return 1; } (defn f [] 1) int g(int i) { return i; } (defn g [i] i)
حالا باید درک تابع square
برای ما آسان باشد. ما آرگومان ورودی x
را میگیریم و (x x *)
را برمیگردانیم. به همین سادگی.
عبارتهای شرطی
عبارت if
در کلوژر وجود دارد ولی متفاوت از کلیدواژه if در جاوا عمل میکند. در حقیقت عبارت های if
مشابه عملگر سهعملوندی در جاوا هستند. بر اساس شرط دادهشده، یکی از دو مقدار را برمیگرداند. برای مثال:
۱. if را فراخوانی میکنیم:
(if ...)
۲. سپس شرط را مینویسیم:
(if condition ...)
۳. سپس مقداری میآید که در صورت true بودن مقدار شرط باید برگردد:
(if condition then)
۴. در نهایت، مقداری که باید در زمان false بودن مقدار باید برگردد را میگذاریم:
(if condition then else)
یک مثال میتواند به فهم کمک شایانی کند:
(if (> x 0) "positive" "zero or negative") (if (< x 0) "negative" "zero or positive")
اگر بخواهیم ترکیب و زنجیرهای از شرطها مثل if else در جاوا داشته باشیم، از cond
استفاده میکنیم. سینکتس cond مانند زیر است:
۱. فرخوانی ماکرو:
(cond ...)
۲. لیستی از شرطها با عبارت مختص هر کدام:
(cond (< x 0) "negative" (> x 0) "positive")
۳. و یک عبارت else اختیاری در صورتی که هیچ کدام از شروط برقرار نبودند:
(cond (< x 0) "negative" (> x 0) "positive" :else "zero")
حل یک مساله واقعی
یادگیری سینتکس در همین حد برای این مقاله کافیست! حالا با همین چیزهایی که یاد گرفتیم، بیایید یک مساله معروف را حل کنیم. مساله Fizz buzz یکی از مسائل آسان برای استفاده از قابلیتهای زبان است. برای مطالعه کامل صورت مساله از این لینک استفاده کنید.
مساله FizzBuzz به طور خلاصه به این شرح است: برنامهای بنویسید که اعداد 1 تا 100 را چاپ کند. به طوری که اگر عدد بر 3 بخشپذیر بود، به جای آن عدد، عبارت Fizz و اگر بر 5 بخشپذیر بود به جای آن عبارت Buzz و اگر هم بر 3 و هم بر 5 بخشپذیر بود، به جای آن عبارت FizzBuzz را چاپ کند.
ما با تعریف تابع شروع میکنیم که یک ورودی میگیرد و همان را برمیگرداند.
(defn fizzbuzz [x] x)
این تابع برای حالتهای غیر بخشپذیر جواب درست برمیگرداند. حالا بیاید قسمت Fizz را اضافه کنیم:
(defn fizzbuzz [x] (if (= x 3) "Fizz" x))
این یک راه بدیهی و نادرست است چرا که برای اعداد ۶ و ۹ و غیره درست جواب نمیدهد ولی مشکل آن را جلوتر حل میکنیم. فعلا بیاید Buzz را اضافه کنیم:
(defn fizzbuzz [x] (cond (= x 3) "Fizz" (= x 5) "Buzz" :else x))
حالا که Buzz را هم اضافه کردیم. با استفاده از ساختار cond، اضافه کردن FizzBuzz هم ساده است:
(defn fizzbuzz [x] (cond (= x 3) "Fizz" (= x 5) "Buzz" (= x 15) "FizzBuzz" :else x))
عالی است! اکنون لازم است بخشپذیری را اضافه کنیم. برای این کار میتوانیم از تابع mod استفاده کنیم:
(defn fizzbuzz [x] (cond (= 0 (mod x 15)) "FizzBuzz" (= 0 (mod x 3)) "Fizz" (= 0 (mod x 5)) "Buzz" :else x))
توجه کنید که ما حالت FizzBuzz را به بالا منتقل کردیم چرا که میخواهیم قبل از حالتهای Fizz و Buzz چک شود.
تمام شد! ما مساله را حل کردیم.
خلاصه
با این آشنایی اولیه، دیدیم که کلوژر زبان سادهایست. ساختارهای اصلی مثل تعریف تابع، عملیاتهای ریاضی یا شرطها بسیار شبیه باقی زبانهای برنامهنویسی هستند. چیزی که اهمیت دارد، این است که کلوژر میتواند به راحتی مسائل فوق العاده پیچیده مثل fizz buzz (!) را حل کند. امیدوارم از این مطلب لذت برده باشید.
پیشنهاد تماشای ویدیو
منبع: dzone.com
.
.
با ما همراه باشید
کانال تلگرام: JavaCupIR@
اکانت توییتر: JavaCupIR@
صفحه اینستاگرام: javacup.ir
صفحه ویرگول: javcup
گروه لینکدین: Iranian Java Developers
خیلی ممنون عالی و مفید بود، همیشه این مدل سینتکس برای یک علامت سوال بزرگ بود (•_•)
سلام. ممنون که مطالعه کردید.