আমি সহজ ভাষায় ব্যাখ্যা দেওয়ার চেষ্টা করব। অন্যরা যেমন উল্লেখ করেছে, মাথা স্বাভাবিক ফর্মটি হাস্কেলের ক্ষেত্রে প্রযোজ্য না, তাই আমি এটি এখানে বিবেচনা করব না।
সাধারণ ফর্ম
সাধারণ ফর্মের একটি এক্সপ্রেশন সম্পূর্ণরূপে মূল্যায়ন করা হয় এবং কোনও উপ-এক্সপ্রেশনকে আর মূল্যায়ন করা যায়নি (যেমন এটির কোনও মূল্যায়ন-বিশিষ্ট অংশ নেই)।
এই অভিব্যক্তিগুলি সমস্ত স্বাভাবিক আকারে:
42
(2, "hello")
\x -> (x + 1)
এই অভিব্যক্তিগুলি স্বাভাবিক আকারে নয়:
1 + 2 -- we could evaluate this to 3
(\x -> x + 1) 2 -- we could apply the function
"he" ++ "llo" -- we could apply the (++)
(1 + 1, 2 + 2) -- we could evaluate 1 + 1 and 2 + 2
দুর্বল মাথা স্বাভাবিক ফর্ম
দুর্বল মাথা স্বাভাবিক ফর্মের একটি অভিব্যক্তি বহিরাগত ডেটা কনস্ট্রাক্টর বা ল্যাম্বদা অ্যাবস্ট্রাকশন ( মাথা ) থেকে মূল্যায়ন করা হয়েছে । সাব-এক্সপ্রেশন মূল্যায়ন করা হতে পারে বা নাও হতে পারে । অতএব, প্রতিটি স্বাভাবিক ফর্ম প্রকাশটি দুর্বল মাথার স্বাভাবিক আকারেও থাকে, যদিও বিপরীতটি সাধারণভাবে ধারণ করে না।
কোনও অভিব্যক্তি দুর্বল মাথা স্বাভাবিক আকারে আছে কিনা তা নির্ধারণ করতে, আমাদের কেবলমাত্র অভিব্যক্তির বাইরের অংশটি দেখতে হবে। যদি এটি ডেটা কনস্ট্রাক্টর বা ল্যাম্বডা হয় তবে এটি দুর্বল মাথার স্বাভাবিক আকারে। এটি যদি কোনও ফাংশন অ্যাপ্লিকেশন হয় তবে তা নয়।
এই অভিব্যক্তিগুলি দুর্বল মাথা স্বাভাবিক আকারে:
(1 + 1, 2 + 2) -- the outermost part is the data constructor (,)
\x -> 2 + 2 -- the outermost part is a lambda abstraction
'h' : ("e" ++ "llo") -- the outermost part is the data constructor (:)
উল্লিখিত হিসাবে, উপরে উল্লিখিত সমস্ত সাধারণ ফর্ম এক্সপ্রেশনগুলিও দুর্বল মাথা স্বাভাবিক ফর্মে।
এই অভিব্যক্তিগুলি দুর্বল মাথা স্বাভাবিক আকারে নয়:
1 + 2 -- the outermost part here is an application of (+)
(\x -> x + 1) 2 -- the outermost part is an application of (\x -> x + 1)
"he" ++ "llo" -- the outermost part is an application of (++)
স্ট্যাক ওভারফ্লোস
দুর্বল মাথা স্বাভাবিক ফর্ম একটি অভিব্যক্তি মূল্যায়নের জন্য অন্যান্য এক্সপ্রেশন প্রথমে ডাব্লুএনএইচএফ মূল্যায়ন করা প্রয়োজন হতে পারে। উদাহরণস্বরূপ, 1 + (2 + 3)
ডাব্লুএইচএনএফকে মূল্যায়ন করার জন্য, আমাদের প্রথমে মূল্যায়ন করতে হবে 2 + 3
। যদি কোনও একক অভিব্যক্তি মূল্যায়নের ফলে নেস্টেড এই মূল্যায়নের অনেকগুলি বাড়ে, ফলাফলটি স্ট্যাকের ওভারফ্লো।
এটি ঘটে যখন আপনি একটি বৃহত্তর অভিব্যক্তি তৈরি করেন যা কোনও ডেটা কনস্ট্রাক্টর বা ল্যাম্বডাস উত্পাদন করে না যতক্ষণ না এর একটি বড় অংশ মূল্যায়ন না করা হয়। এগুলি প্রায়শই এই ধরণের ব্যবহারের কারণে ঘটে foldl
:
foldl (+) 0 [1, 2, 3, 4, 5, 6]
= foldl (+) (0 + 1) [2, 3, 4, 5, 6]
= foldl (+) ((0 + 1) + 2) [3, 4, 5, 6]
= foldl (+) (((0 + 1) + 2) + 3) [4, 5, 6]
= foldl (+) ((((0 + 1) + 2) + 3) + 4) [5, 6]
= foldl (+) (((((0 + 1) + 2) + 3) + 4) + 5) [6]
= foldl (+) ((((((0 + 1) + 2) + 3) + 4) + 5) + 6) []
= (((((0 + 1) + 2) + 3) + 4) + 5) + 6
= ((((1 + 2) + 3) + 4) + 5) + 6
= (((3 + 3) + 4) + 5) + 6
= ((6 + 4) + 5) + 6
= (10 + 5) + 6
= 15 + 6
= 21
লক্ষ করুন যে কীভাবে এটি গভীরভাবে যেতে হবে তার আগে এটি দুর্বল মাথাটিকে স্বাভাবিক আকারে ফেলে।
আপনি ভাবতে পারেন, হাস্কেল কেন আগে সময়ের অভ্যন্তরীণ ভাবগুলি কমায় না? এটি হাস্কেলের অলসতার কারণে। যেহেতু এটি সাধারণভাবে অনুমান করা যায় না যে প্রতিটি সূক্ষ্ম এক্সপ্রেশন প্রয়োজন হবে, তাই এক্সপ্রেশনগুলি বাইরে থেকে মূল্যায়ন করা হয়।
(জিএইচসির একটি কঠোরতা বিশ্লেষক রয়েছে যা এমন কিছু পরিস্থিতি সনাক্ত করতে পারে যেখানে একটি subexpression সবসময় প্রয়োজন হয় এবং এটি এর আগে সময়ের আগে এটি মূল্যায়ন করতে পারে This তবে এটি কেবলমাত্র একটি অপ্টিমাইজেশন, তবে আপনাকে ওভারফ্লো থেকে রক্ষা করার জন্য এটির উপর নির্ভর করা উচিত নয়)।
অন্যদিকে, এই ধরণের অভিব্যক্তি সম্পূর্ণ নিরাপদ:
data List a = Cons a (List a) | Nil
foldr Cons Nil [1, 2, 3, 4, 5, 6]
= Cons 1 (foldr Cons Nil [2, 3, 4, 5, 6]) -- Cons is a constructor, stop.
এই বৃহত্তর অভিব্যক্তিগুলি তৈরি করা এড়াতে যখন আমরা জানি যে সমস্ত স্যুপ এক্সপ্রেসনগুলি মূল্যায়ন করতে হবে, আমরা অভ্যন্তরীণ অংশগুলি সময়ের আগে মূল্যায়ন করতে বাধ্য করতে চাই।
seq
seq
হ'ল একটি বিশেষ ফাংশন যা মূল্যায়ন করার জন্য অভিব্যক্তিগুলিকে বাধ্য করার জন্য ব্যবহৃত হয়। এর শব্দার্থবিজ্ঞানের seq x y
অর্থ হ'ল যখনই y
দুর্বল মাথার স্বাভাবিক ফর্মকে x
মূল্যায়ন করা হয় তখন দুর্বল মাথার স্বাভাবিক ফর্মকেও মূল্যায়ন করা হয়।
এটি সংজ্ঞা হিসাবে ব্যবহৃত অন্যান্য জায়গাগুলির মধ্যে foldl'
, এর কঠোর রূপ foldl
।
foldl' f a [] = a
foldl' f a (x:xs) = let a' = f a x in a' `seq` foldl' f a' xs
প্রতিটি পুনরাবৃত্তি foldl'
সঞ্চালককে ডাব্লুএইচএনএফ-তে বাধ্য করে। এটি এটির ফলে একটি বৃহত্তর অভিব্যক্তি তৈরি করা এড়ানো যায় এবং তাই এটি স্ট্যাককে উপচে পড়া এড়ানো।
foldl' (+) 0 [1, 2, 3, 4, 5, 6]
= foldl' (+) 1 [2, 3, 4, 5, 6]
= foldl' (+) 3 [3, 4, 5, 6]
= foldl' (+) 6 [4, 5, 6]
= foldl' (+) 10 [5, 6]
= foldl' (+) 15 [6]
= foldl' (+) 21 []
= 21 -- 21 is a data constructor, stop.
তবে হাস্কেলউইকির উদাহরণ হিসাবে উল্লেখ করা হয়েছে, এটি সমস্ত ক্ষেত্রে আপনাকে রক্ষা করতে পারে না, কারণ সঞ্চয়ের শুধুমাত্র ডাব্লুএইচএনএফকে মূল্যায়ন করা হয়। উদাহরণে, সঁচায়ক তাই এটি শুধুমাত্র tuple কন্সট্রাকটর মূল্যায়ন, এবং বাধ্য করা হবে, একটি tuple হয় acc
বা len
।
f (acc, len) x = (acc + x, len + 1)
foldl' f (0, 0) [1, 2, 3]
= foldl' f (0 + 1, 0 + 1) [2, 3]
= foldl' f ((0 + 1) + 2, (0 + 1) + 1) [3]
= foldl' f (((0 + 1) + 2) + 3, ((0 + 1) + 1) + 1) []
= (((0 + 1) + 2) + 3, ((0 + 1) + 1) + 1) -- tuple constructor, stop.
এটি এড়াতে, আমাদের অবশ্যই এটি তৈরি করতে হবে যাতে টিউপল কনস্ট্রাক্টর মূল্যায়নের জন্য acc
এবং এর মূল্যায়ন জোর করে len
। আমরা এটি ব্যবহার করে করি seq
।
f' (acc, len) x = let acc' = acc + x
len' = len + 1
in acc' `seq` len' `seq` (acc', len')
foldl' f' (0, 0) [1, 2, 3]
= foldl' f' (1, 1) [2, 3]
= foldl' f' (3, 2) [3]
= foldl' f' (6, 3) []
= (6, 3) -- tuple constructor, stop.