পুনরাবৃত্তি বাস্তবায়নের জন্য হাস্কেল অলস-মূল্যায়ন ব্যবহার করে, তাই যখন প্রয়োজন হয় তখন কোনও মূল্য সরবরাহ করার প্রতিশ্রুতি হিসাবে যেকোন কিছু বিবেচনা করে (এটি একটি থাঙ্ক বলা হয়)। ভাবগুলি আরও এগিয়ে যাওয়ার জন্য যতটা প্রয়োজন তত হ্রাস পাবে। এটি গাণিতিকভাবে আপনি কোনও অভিব্যক্তি সহজতর করার সাথে সাদৃশ্যপূর্ণ, সুতরাং এটি সেভাবে ভাবতে সহায়তা করে। আপনার কোড দ্বারা মূল্যায়নের আদেশ নির্দিষ্ট করা হয়নি তা কম্পাইলারটি আপনাকে ব্যবহৃত লেজ-কল নির্মূলকরণের চেয়ে প্রচুর চালক অপটিমাইজেশন করতে দেয়। আপনি অপ্টিমাইজেশন চান সঙ্গে সংকলন -O2
!
আসুন দেখুন কীভাবে আমরা facSlow 5
কেস স্টাডি হিসাবে মূল্যায়ন করি :
facSlow 5
5 * facSlow 4
5 * (4 * facSlow 3)
5 * (4 * (3 * facSlow 2))
5 * (4 * (3 * (2 * facSlow 1)))
5 * (4 * (3 * (2 * 1)))
5 * (4 * (3 * 2))
5 * (4 * 6)
5 * 24
120
তাই হিসাবে আপনি চিন্তিত, আমরা একটি বিল্ড-আপ সংখ্যার আগে কোনো গণনার ঘটতে আছে, কিন্তু অসদৃশ আপনি চিন্তিত, সেখানে কোন স্ট্যাক এর facSlow
বিনষ্ট অপেক্ষা প্রায় ঝুলন্ত ফাংশন কল - প্রতিটি হ্রাস প্রয়োগ করা হয় এবং চলে যায়, একটি ছাড়ার সময় স্ট্যাক ফ্রেম তার জাগ (এটি কারণ (*)
কঠোর এবং এটির দ্বিতীয় তর্কটির মূল্যায়ন ট্রিগার করে)।
হাস্কেলের পুনরাবৃত্ত ফাংশনগুলি খুব পুনরাবৃত্তির উপায়ে মূল্যায়ন করা হয় না! কেবল চারদিকে ঝুলন্ত কলগুলির স্ট্যাক হ'ল গুণগুলি themselves যদি (*)
কোনও কঠোর ডেটা কনস্ট্রাক্টর হিসাবে দেখা হয় তবে এটি রক্ষিত পুনরাবৃত্তি হিসাবে পরিচিত (যদিও এটি সাধারণত নন- স্ট্রাক্ট ডেটা কনস্ট্রাক্টর হিসাবে পরিচিত হয় , যেখানে এর পরিপ্রেক্ষিতে যা অবশিষ্ট থাকে তা ডেটা কনস্ট্রাক্টর হয় - যখন আরও অ্যাক্সেস দ্বারা বাধ্য করা হয়)।
এবার লেজ-পুনরাবৃত্তির দিকে নজর দেওয়া যাক fac 5
:
fac 5
fac' 5 1
fac' 4 {5*1}
fac' 3 {4*{5*1}}
fac' 2 {3*{4*{5*1}}}
fac' 1 {2*{3*{4*{5*1}}}}
{2*{3*{4*{5*1}}}}
(2*{3*{4*{5*1}}})
(2*(3*{4*{5*1}}))
(2*(3*(4*{5*1})))
(2*(3*(4*(5*1))))
(2*(3*(4*5)))
(2*(3*20))
(2*60)
120
সুতরাং আপনি দেখতে পাচ্ছেন কীভাবে নিজেই লেজ পুনরাবৃত্তি আপনাকে কোনও সময় বা স্থান সংরক্ষণ করে নি। এটি সামগ্রিকভাবে কেবল আরও পদক্ষেপ গ্রহণ করে না facSlow 5
, এটি নেস্টেড থাঙ্কও তৈরি করে (এখানে দেখানো হয়েছে {...}
) - এটির জন্য অতিরিক্ত স্থানের প্রয়োজন - যা ভবিষ্যতের গণনা, নেস্টেড গুণগুলি সম্পাদন করার জন্য বর্ণনা করে।
এই thunk তারপর ঢোঁড়ন দ্বারা উন্মোচন করা হয় এটা নীচে, স্ট্যাক গণনার recreating। উভয় সংস্করণের জন্য খুব দীর্ঘ গণনাগুলির সাথে স্ট্যাক ওভারফ্লো তৈরির একটি বিপদও রয়েছে।
আমরা যদি এটি হ্যান্ড-অপ্টিমাইজ করতে চাই তবে আমাদের কেবল এটি কঠোর করা দরকার। আপনি $!
সংজ্ঞায়িত করতে কঠোর অ্যাপ্লিকেশন অপারেটরটি ব্যবহার করতে পারেন
facSlim :: (Integral a) => a -> a
facSlim x = facS' x 1 where
facS' 1 y = y
facS' x y = facS' (x-1) $! (x*y)
এটি facS'
তার দ্বিতীয় যুক্তিতে কঠোর হতে বাধ্য করে। (এটি তার প্রথম যুক্তিতে ইতিমধ্যে কঠোর কারণ কোন সংজ্ঞা facS'
প্রয়োগ করতে হবে তা নির্ধারণের জন্য এটি মূল্যায়ন করতে হবে ।)
কখনও কখনও কঠোরতা বিপুল পরিমাণে সাহায্য করতে পারে, কখনও কখনও এটি একটি বড় ভুল কারণ অলসতা আরও দক্ষ। এখানে এটি একটি ভাল ধারণা:
facSlim 5
facS' 5 1
facS' 4 5
facS' 3 20
facS' 2 60
facS' 1 120
120
আপনি যা অর্জন করতে চেয়েছিলেন তা আমি মনে করি।
সারসংক্ষেপ
- আপনি যদি নিজের কোডটি অপ্টিমাইজ করতে চান তবে প্রথম ধাপটি সংকলন করা
-O2
- লেজ পুনরাবৃত্তি কেবল তখনই ভাল যখন কোনও থ্রুক বিল্ড-আপ নেই এবং কঠোরতা যুক্ত করা সাধারণত এটি এবং যদি উপযুক্ত হয় তবে এটি প্রতিরোধ করতে সহায়তা করে। আপনি যখন এমন ফলাফল তৈরি করছেন যা পরে একবারে একবারে প্রয়োজন হয় This
- কখনও কখনও লেজ পুনরাবৃত্তি একটি খারাপ পরিকল্পনা এবং রক্ষিত পুনরাবৃত্তি একটি ভাল ফিট, অর্থাত্ যখন আপনি ফলাফল তৈরি করছেন কিছুটা ধাপে প্রয়োজন হবে। এই প্রশ্নটি সম্পর্কে
foldr
এবং foldl
উদাহরণস্বরূপ দেখুন এবং একে অপরের বিরুদ্ধে তাদের পরীক্ষা করুন।
এই দুটি চেষ্টা করুন:
length $ foldl1 (++) $ replicate 1000
"The size of intermediate expressions is more important than tail recursion."
length $ foldr1 (++) $ replicate 1000
"The number of reductions performed is more important than tail recursion!!!"
foldl1
লেজ পুনরাবৃত্ত হয়, যেখানে foldr1
রক্ষিত পুনরাবৃত্তি সম্পাদন করে যাতে প্রথম আইটেমটি তাত্ক্ষণিকভাবে আরও প্রক্রিয়াজাতকরণ / অ্যাক্সেসের জন্য উপস্থাপিত হয়। (প্রথম "বন্ধুত্বের" একবারে বাম দিকে, (...((s+s)+s)+...)+s
তার ইনপুট তালিকাটি সম্পূর্ণরূপে শেষের দিকে চাপিয়ে দেয় এবং তার সম্পূর্ণ ফলাফলের প্রয়োজনের তুলনায় অনেক তাড়াতাড়ি ভবিষ্যতের গণনার বৃহত অংশ তৈরি করে; দ্বিতীয় s+(s+(...+(s+s)...))
ইনপুট গ্রহণ করে ধীরে ধীরে ডানদিকে প্রথম বন্ধনীরূপীকরণ করে) পর্যায়ক্রমে তালিকাবদ্ধ করুন, যাতে পুরো জিনিসটি অপ্টিমাইজেশন সহ ধ্রুবক স্থানে পরিচালনা করতে সক্ষম হয়)।
আপনি কোন হার্ডওয়্যার ব্যবহার করছেন তার উপর নির্ভর করে আপনার জিরো সংখ্যা সামঞ্জস্য করতে হবে।