۷ - ۹سبک بینقطه
این سبک به ترکیب توابع، بدون مشخص کردن آرگومانهاشون اطلاق میشه. برخلاف چیزی که ممکنه به نظر برسه، منظور از "نقطه" عملگر ِ ترکیبِ تابع (.
) نیست، بلکه نقطه به آرگومانها اشاره داره. به کلامی، ما "نقطه" (عملگر) اضافه میکنیم تا بتونیم نقطهها (آرگومانها) رو کَم کنیم. اکثر مواقع کُدِ بینقطه خیلی جمعوجور و خواناتر میشه، و به خواننده کمک میکنه که بیشتر روی توابع تمرکز کنه تا دادهها.
بالاتر گفتیم که ترکیب توابع این شِکلیِه:
(f . g) x = f (g x)
هر چقدر تابعهای بیشتری با هم ترکیب شن، اینطور ترکیب هم خواناییشون رو بیشتر میکنه. برای مثال، (f. g. h) x
نسبت به (f (g (h x))
خواناییِ بیشتری داره و تمرکز رو هم بیشتر به تابع منعطف میکنه تا آرگومانها. سبک بینقطه در واقع یه بَسطی از همین ایدهست، فقط آرگومانها کلاً حذف میشن:
f . g = \x -> f (g x)
f . g . h = \x -> f (g (h x))
با بازنویسی چندتا از مثالهای بخش قبل به سبکِ بینقطه شروع میکنیم تا این سبک رو در عمل ببینیم:
Prelude> let f = negate . sum
Prelude> f [1, 2, 3, 4, 5]
-15
دقت کنین که در تعریف تابعِ f
هیچ پارامتری مشخص نکردیم. ولی با اعمال ِ اون تابع به یه آرگومان، مثل قبل کار میکنه.
این تابع رو
f :: Int -> [Int] -> Int
f z xs = foldr (+) z xs
چطور به سبک بینقطه بنویسیم؟
Prelude> let f = foldr (+)
Prelude> f 0 [1..5]
15
حالا که برای تابع اسم تعریف کردیم، میشه با آرگومانهای دیگه ازش استفاده کرد.
مثالی دیگه از یه تابعِ کوتاه و بینقطه به همراه نتیجهش. برای این تابع دوباره از filter
استفاده میکنیم، اما این بار با عملگر ِ ==
که Bool
برمیگردونه. با دقت بهش نگاه کنین، و تو ذهنتون یا روی کاغذ، پروسهی محاسبهش رو پیش برین:
Prelude> let f = length . filter (== 'a')
Prelude> f "abracadabra"
5
در مثال بعدی، یک مجموعه از توابع رو میبینیم که در یه ماژول با هم کار میکنن که هم به ترکیب و هم به سبک ِ بینقطه اتکا دارن:
-- arith2.hs
module Arith2 where
add :: Int -> Int -> Int
add x y = x + y
addPF :: Int -> Int -> Int
addPF = (+)
addOne :: Int -> Int
addOne = \x -> x + 1
addOnePF :: Int -> Int
addOnePF = (+1)
main :: IO ()
main = do
print (0 :: Int)
print (add 1 0)
print (addOne 0)
print (addOnePF 0)
print ((addOne . addOne) 0)
print ((addOnePF . addOne) 0)
print ((addOne . addOnePF) 0)
print ((addOnePF . addOnePF) 0)
print (negate (addOne 0))
print ((negate . addOne) 0)
print ((addOne . addOne . addOne
. negate . addOne) 0)
با حوصله ببینین تکتک توابع چه کاری انجام میدن، چه رو کاغذ چه تو ذهنتون. بعد هم فایلش رو در GHCi بارگذاری کنین و جوابهاتون رو چک کنین.
احتمالاً تا الان درک خوبی از نحوهی ترکیب توابع با (.)
رو پیدا کردین. مهم هست که یادتون بمونه توابع در یک ترکیب، از راست به چپ اعمال میشن، مثل پَکمَن* که از راست بِجَوه، و همینطور که میره بیانیهها رو ساده کنه.
م. یا به قولِ خودمون، نقطهخور!