আমি দীর্ঘদিন ধরে ভাবছিলাম যে অলস মূল্যায়ন কেন কার্যকর। আমার কাছে এখনও কেউ এমন কিছু বোঝাতে পেরেছিল যা বোঝার জন্য; বেশিরভাগ ক্ষেত্রে এটি "আমাকে বিশ্বাস করুন" এ ফুটে ওঠে।
দ্রষ্টব্য: আমি স্মৃতিচারণ বলতে চাই না।
আমি দীর্ঘদিন ধরে ভাবছিলাম যে অলস মূল্যায়ন কেন কার্যকর। আমার কাছে এখনও কেউ এমন কিছু বোঝাতে পেরেছিল যা বোঝার জন্য; বেশিরভাগ ক্ষেত্রে এটি "আমাকে বিশ্বাস করুন" এ ফুটে ওঠে।
দ্রষ্টব্য: আমি স্মৃতিচারণ বলতে চাই না।
উত্তর:
বেশিরভাগ কারণ এটি আরও দক্ষ হতে পারে - মানগুলি সেগুলি ব্যবহার না করা হলে গণনা করার প্রয়োজন নেই। উদাহরণস্বরূপ, আমি কোনও ফাংশনে তিনটি মান পাস করতে পারি, তবে শর্তাধীন অভিব্যক্তির ক্রমের উপর নির্ভর করে কেবলমাত্র একটি উপসেট ব্যবহার করা যেতে পারে। সি এর মতো একটি ভাষায়, তিনটি মানই যে কোনওভাবেই গণনা করা হবে; তবে হাস্কেলগুলিতে কেবল প্রয়োজনীয় মানগুলি গণনা করা হয়।
এটি অসীম তালিকার মতো দুর্দান্ত স্টাফের জন্যও অনুমতি দেয়। সি এর মতো ভাষায় আমার কাছে অসীম তালিকা থাকতে পারে না, তবে হাস্কেলে, এটি কোনও সমস্যা নয়। অসীম তালিকা গণিতের নির্দিষ্ট কিছু ক্ষেত্রে প্রায়শই প্রায়শই ব্যবহার করা হয়, সুতরাং এগুলিকে ম্যানিপুলেট করার ক্ষমতা রাখাই কার্যকর হতে পারে।
অলস মূল্যায়নের একটি দরকারী উদাহরণ হ'ল quickSort
:
quickSort [] = []
quickSort (x:xs) = quickSort (filter (< x) xs) ++ [x] ++ quickSort (filter (>= x) xs)
আমরা যদি এখন তালিকার সর্বনিম্ন সন্ধান করতে চাই তবে আমরা সংজ্ঞা দিতে পারি
minimum ls = head (quickSort ls)
যা প্রথমে তালিকাটিকে বাছাই করে এবং তারপরে তালিকার প্রথম উপাদানটি গ্রহণ করে। যাইহোক, অলস মূল্যায়নের কারণে, কেবল মাথা গুনে যায়। উদাহরণস্বরূপ, আমরা যদি সর্বনিম্ন তালিকার তালিকার নিই তবে [2, 1, 3,]
কুইকসোর্ট প্রথমে দুটি এর চেয়ে ছোট সমস্ত উপাদান ফিল্টার করে ফেলবে। তারপরে এটি কুইকসোর্ট করে (সিঙ্গলটন তালিকা [1] ফিরিয়ে দেয়) যা ইতিমধ্যে যথেষ্ট। অলস মূল্যায়নের কারণে, বাকীগুলি কখনই বাছাই করা হয় না, প্রচুর গণনার সময় সাশ্রয় করে।
এটি অবশ্যই খুব সাধারণ উদাহরণ, তবে অলসতা খুব বড় প্রোগ্রামগুলির জন্য একইভাবে কাজ করে।
তবে, এই সমস্তগুলির একটি বিপর্যয় রয়েছে: আপনার প্রোগ্রামটির রানটাইম গতি এবং মেমরির ব্যবহারের পূর্বাভাস দেওয়া আরও শক্ত হয়ে যায়। এর অর্থ এই নয় যে অলস প্রোগ্রামগুলি ধীর হয় বা আরও মেমরি নেয় তবে এটি জেনে রাখা ভাল।
take k $ quicksort list
কেবল ও (এন + কে লগ কে) সময় নেয়, যেখানে n = length list
। একটি অলস তুলনা সাজানোর সাথে, এটি সর্বদা ও (এন লগ এন) সময় নেয়।
আমি অলস মূল্যায়ন বেশ কয়েকটি জিনিসের জন্য দরকারী বলে মনে করি।
প্রথমত, সমস্ত বিদ্যমান অলস ভাষাগুলি খাঁটি, কারণ অলস ভাষায় পার্শ্ব প্রতিক্রিয়া সম্পর্কে যুক্তি করা খুব কঠিন।
খাঁটি ভাষাগুলি আপনাকে সমতুল্য যুক্তি ব্যবহার করে ফাংশন সংজ্ঞা সম্পর্কে যুক্তি দেয়।
foo x = x + 3
দুর্ভাগ্যক্রমে একটি অলস সেটিংয়ে, অলস সেটিংয়ের তুলনায় আরও বিবৃতি ফিরে আসতে ব্যর্থ হয়, সুতরাং এমএল এর মতো ভাষায় এটি কম কার্যকর। তবে অলস ভাষায় আপনি সমতা সম্পর্কে নিরাপদে যুক্তি দিতে পারেন।
দ্বিতীয়ত, এমএল-তে 'মান সীমাবদ্ধতার' মতো প্রচুর স্টাফ হাস্কেলের মতো অলস ভাষায় প্রয়োজন হয় না। এটি সিনট্যাক্সের দুর্দান্ত ডিক্লুটটারিংয়ের দিকে নিয়ে যায়। ভাষার মতো এমএলগুলিতে ভের বা মজাদার কীওয়ার্ড ব্যবহার করা দরকার। হাসকেলে এই জিনিসগুলি একটি ধারণার মধ্যে পড়ে যায়।
তৃতীয়, অলসতা আপনাকে খুব কার্যকরী কোড লিখতে দেয় যা টুকরোয় বোঝা যায়। হাস্কেলের মধ্যে একটি ফাংশন বডি যেমন লেখা সাধারণত:
foo x y = if condition1
then some (complicated set of combinators) (involving bigscaryexpression)
else if condition2
then bigscaryexpression
else Nothing
where some x y = ...
bigscaryexpression = ...
condition1 = ...
condition2 = ...
এটি আপনাকে 'টপ ডাউন' কাজ করতে দেয় যদিও কোনও ফাংশনটির বডি বোঝার জন্য। এমএল-এর মতো ভাষা আপনাকে let
কঠোরভাবে মূল্যায়িত এমন একটি ব্যবহার করতে বাধ্য করে। ফলস্বরূপ, আপনি ফাংশনটির মূল অংশটি 'লিফট' করার সাহস করবেন না কারণ এটি ব্যয়বহুল (বা পার্শ্ব প্রতিক্রিয়া থাকলে) আপনি চান না যে এটি সর্বদা মূল্যায়ন করা হোক। হাস্কেল বিশদটি যেখানে স্পষ্টভাবে বিবরণটি 'ধাক্কা' দিতে পারে কারণ এটি জানে যে cla ধারাটির বিষয়বস্তু কেবল প্রয়োজন হিসাবে মূল্যায়ন করা হবে।
অনুশীলনে, আমরা প্রহরী ব্যবহার করে এবং ধসের এই প্রবণতাটি আরও:
foo x y
| condition1 = some (complicated set of combinators) (involving bigscaryexpression)
| condition2 = bigscaryexpression
| otherwise = Nothing
where some x y = ...
bigscaryexpression = ...
condition1 = ...
condition2 = ...
চতুর্থত, অলসতা কখনও কখনও নির্দিষ্ট অ্যালগরিদমের অনেক বেশি মার্জিত অভিব্যক্তি দেয়। হাস্কেলের একটি অলস 'কুইক সার্ট' হ'ল এক-লাইনার এবং এর সুবিধা রয়েছে যে আপনি যদি কেবল প্রথম কয়েকটি আইটেম দেখেন তবে কেবলমাত্র সেই আইটেমগুলি নির্বাচনের ব্যয়ের সাথে আনুপাতিক ব্যয় আপনি প্রদান করেন। কোনও কিছুই আপনাকে কঠোরভাবে এটি করতে বাধা দেয় না, তবে একই অ্যাসিপোটোটিক পারফরম্যান্স অর্জনের জন্য আপনাকে প্রতিবার অ্যালগরিদমটি পুনর্নির্মাণ করতে হবে।
পঞ্চম, অলসতা আপনাকে ভাষাতে নতুন নিয়ন্ত্রণ কাঠামো সংজ্ঞায়িত করতে দেয়। আপনি কোনও 'যদি .. তবে .. অন্যথায় ..' লিখতে পারবেন না, যেমন কঠোর ভাষায় লিখুন। আপনি যদি কোনও ফাংশন সংজ্ঞায়িত করার চেষ্টা করেন তবে:
if' True x y = x
if' False x y = y
কঠোর ভাষায় উভয় শাখার শর্ত মান নির্বিশেষে মূল্যায়ন করা হবে। আপনি লুপগুলি বিবেচনা করলে এটি আরও খারাপ হয়। সমস্ত কঠোর সমাধানগুলির জন্য আপনাকে কিছু প্রকারের উদ্ধৃতি বা সুস্পষ্ট ল্যাম্বদা নির্মাণ সরবরাহ করতে ভাষা প্রয়োজন।
অবশেষে, একই শিরাতে, টাইপ সিস্টেমে যেমন ম্যানডসের পার্শ্ব-প্রতিক্রিয়াগুলি মোকাবেলার জন্য সেরা কয়েকটি পদ্ধতি সত্যই কেবল অলস সেটিংয়ে কার্যকরভাবে প্রকাশ করা যেতে পারে। এফ # এর কার্যপ্রবাহের জটিলতা হাস্কেল মনাদসের সাথে তুলনা করে এটি প্রত্যক্ষ করা যেতে পারে। (আপনি একটি কঠোর ভাষায় একটি monad সংজ্ঞা দিতে পারেন, কিন্তু দুর্ভাগ্যক্রমে আপনি প্রায়শই একটি monad আইন ব্যর্থ করতে পারেন তুলনায় অলসতা এবং কর্মপ্রবাহের অভাবের কারণে এক টন কঠোর লাগেজ তুলুন।)
let
পুনরুক্তি করা একটি বিপজ্জনক জন্তু, আর 6 আরএস স্কিমে এটি এলোমেলোভাবে #f
আপনার শব্দটিতে উপস্থিত হতে দেয় যেখানেই গিঁট বেঁধে কঠোরভাবে চক্র পরিচালিত হয়! কোনও পাং উদ্দেশ্য নয়, তবে কঠোরভাবে আরও পুনরাবৃত্ত let
বাইন্ডিংগুলি অলস ভাষায় বোধগম্য। কঠোরতা এই সত্যটিকে আরও বাড়িয়ে তোলে যে এসসিসির where
ব্যতীত আপেক্ষিক প্রভাবগুলি অর্ডার করার কোনও উপায় নেই, এটি একটি বিবৃতি স্তরের নির্মাণ, এর প্রভাবগুলি কোনও ক্রমে কঠোরভাবে ঘটতে পারে এবং এমনকি যদি আপনার খাঁটি ভাষা থাকে তবে আপনি #f
সমস্যা. যথাযথwhere
অ স্থানীয় স্থানীয় উদ্বেগগুলির সাথে আপনার কোডটিকে ধাঁধা দেয়।
ifFunc(True, x, y)
উভয় x
এবং y
পরিবর্তে ন্যায়বিচার মূল্যায়ন করতে চলেছে x
।
সাধারণ অর্ডার মূল্যায়নের মধ্যে একটি অলস মূল্যায়ন (হাস্কেলের মতো) মধ্যে পার্থক্য রয়েছে।
square x = x * x
নিম্নলিখিত এক্সপ্রেশন মূল্যায়ন করা হচ্ছে ...
square (square (square 2))
... আগ্রহী মূল্যায়ন সহ:
> square (square (2 * 2))
> square (square 4)
> square (4 * 4)
> square 16
> 16 * 16
> 256
... সাধারণ ক্রম মূল্যায়ন সহ:
> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * (square (square 2))
> ((2 * 2) * (square 2)) * (square (square 2))
> (4 * (square 2)) * (square (square 2))
> (4 * (2 * 2)) * (square (square 2))
> (4 * 4) * (square (square 2))
> 16 * (square (square 2))
> ...
> 256
... অলস মূল্যায়নের সাথে:
> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * ((square 2) * (square 2))
> ((2 * 2) * (2 * 2)) * ((2 * 2) * (2 * 2))
> (4 * 4) * (4 * 4)
> 16 * 16
> 256
কারণ অলস মূল্যায়ন সিনট্যাক্স ট্রিকে দেখে এবং গাছের রূপান্তর করে ...
square (square (square 2))
||
\/
*
/ \
\ /
square (square 2)
||
\/
*
/ \
\ /
*
/ \
\ /
square 2
||
\/
*
/ \
\ /
*
/ \
\ /
*
/ \
\ /
2
... যেখানে সাধারণ ক্রমের মূল্যায়ন কেবল পাঠ্য বিস্তৃতি করে।
এ কারণেই আমরা, অলস মূল্যায়ন ব্যবহার করার সময়, আরও শক্তিশালী হয়ে উঠি (মূল্যায়ন আরও প্রায়শই অন্যান্য কৌশলগুলি পরে স্থির হয়) যখন কর্মক্ষমতা আগ্রহী মূল্যায়নের সমতুল্য (কমপক্ষে ও-স্বীকৃতিতে)।
র্যাম সম্পর্কিত আবর্জনা সংগ্রহের মতোই সিপিইউ সম্পর্কিত অলস মূল্যায়ন। জিসি আপনাকে ভান করার অনুমতি দেয় যে আপনার কাছে সীমাহীন পরিমাণের মেমরি রয়েছে এবং আপনার প্রয়োজন অনুসারে মেমোরিতে যতগুলি অবজেক্ট অনুরোধ করবেন। রানটাইম স্বয়ংক্রিয়ভাবে ব্যবহারযোগ্য অব্যবহৃত জিনিসগুলির দাবি জানাবে। এলই আপনাকে আপনার কাছে সীমাহীন কম্পিউটেশনাল রিসোর্স রয়েছে তা ভান করার অনুমতি দেয় - আপনি যতগুলি কমপিউটিশন প্রয়োজন তেমন করতে পারেন। রানটাইম কেবল অপ্রয়োজনীয় (প্রদত্ত ক্ষেত্রে) গণনা সম্পাদন করবে না।
এই "ভান" মডেলগুলির ব্যবহারিক সুবিধা কী? এটি সংস্থানগুলি পরিচালনা থেকে বিকাশকারীকে (কিছুটা হলেও) মুক্তি দেয় এবং আপনার উত্স থেকে কিছু বয়লারপ্লেট কোড সরিয়ে দেয়। তবে আরও গুরুত্বপূর্ণটি হ'ল আপনি প্রসঙ্গের বিস্তৃত সেটগুলিতে আপনার সমাধানটি দক্ষতার সাথে পুনরায় ব্যবহার করতে পারেন।
কল্পনা করুন যে আপনার কাছে এস এবং একটি সংখ্যা এন এর একটি তালিকা রয়েছে। আপনাকে তালিকা থেকে এন নম্বর এম এর নিকটতম সন্ধান করতে হবে। আপনার দুটি প্রসঙ্গ থাকতে পারে: একক এন এবং কিছুটা এল এর এন (Ei প্রতিটি এন এর জন্য এনআই) আপনি নিকটতম এম ইন এস সন্ধান করুন)। যদি আপনি অলস মূল্যায়ন ব্যবহার করেন তবে আপনি এস এর বাছাই করতে পারেন এবং এন এর নিকটতম এম সন্ধান করতে বাইনারি অনুসন্ধান প্রয়োগ করতে পারেন। ভাল অলস বাছাইয়ের জন্য এটি সি (এন) (আকার (এস)) * এর জন্য ও (আকার (এস)) পদক্ষেপের প্রয়োজন হবে (আকার (এস) + আকার (এল)) সমানভাবে বিতরণ করা এল এর জন্য পদক্ষেপগুলি। যদি আপনার সর্বোত্তম দক্ষতা অর্জনের জন্য অলস মূল্যায়ন না হয় তবে আপনাকে প্রতিটি প্রসঙ্গের জন্য অ্যালগরিদম প্রয়োগ করতে হবে।
আপনি যদি মনে করেন সাইমন Peyton জোন্স, অলস মূল্যায়ন গুরুত্বপূর্ণ নয় কোনটাই কিন্তু শুধুমাত্র একটি 'চুল শার্ট' যে ডিজাইনার বিশুদ্ধ ভাষা রাখা বাধ্য হয়। আমি নিজেকে এই দৃষ্টিভঙ্গির প্রতি সহানুভূতিশীল মনে করি।
রিচার্ড বার্ড, জন হিউজেস এবং আরও কম প্রসারিত র্যালফ হিনজে অলস মূল্যায়নের মাধ্যমে আশ্চর্যজনক কাজ করতে সক্ষম। তাদের কাজ পড়া আপনাকে এটির প্রশংসা করতে সহায়তা করবে। ভাল শুরু পয়েন্ট বার্ড এর মহৎ সুডোকু Solver এবং এর হিউজেস এর কাগজ কেন কার্যকরী প্রোগ্রামিং ম্যাটার্স ।
IO
মোনাডের প্রবর্তনের আগের ) স্বাক্ষর main
হবে String -> String
এবং আপনি ইতিমধ্যে সঠিকভাবে ইন্টারেক্টিভ প্রোগ্রাম লিখতে পারেন।
IO
?
টিক-ট্যাক-টো প্রোগ্রাম বিবেচনা করুন। এর চারটি ফাংশন রয়েছে:
এটি উদ্বেগের একটি সুন্দর স্পষ্ট বিচ্ছেদ তৈরি করে। বিশেষত মুভ-জেনারেশন ফাংশন এবং বোর্ড মূল্যায়ন ফাংশনগুলি কেবল গেমের নিয়মগুলি বোঝার প্রয়োজন: মুভ ট্রি এবং মিনিম্যাক্স ফাংশনগুলি সম্পূর্ণ পুনরায় ব্যবহারযোগ্য।
এখন টিক-ট্যাক-টোয়ের পরিবর্তে দাবা বাস্তবায়নের চেষ্টা করা যাক। "উত্সাহী" (অর্থাত্ প্রচলিত) ভাষায় এটি কাজ করবে না কারণ মুভ ট্রিটি মেমরির সাথে খাপ খায় না। সুতরাং এখন বোর্ড মূল্যায়ন এবং মুভ জেনারেশন ফাংশনগুলিকে মুভ ট্রি এবং মিনিম্যাক্স লজিকের সাথে মিশ্রিত করা দরকার কারণ কোনটি উত্পন্ন হবে তা নির্ধারণ করতে মিনিম্যাক্স যুক্তি ব্যবহার করতে হবে। আমাদের সুন্দর পরিষ্কার মডুলার কাঠামো অদৃশ্য হয়ে যায়।
তবে অলস ভাষায় মুভি গাছের উপাদানগুলি কেবল মিনিম্যাক্স ফাংশন থেকে দাবির প্রতিক্রিয়া হিসাবে তৈরি করা হয়: উপরের উপাদানটিতে মিনিম্যাক্সটি আলগা করার আগে পুরো মুভ ট্রিটি উত্পন্ন করার প্রয়োজন হয় না। সুতরাং আমাদের পরিষ্কার মডুলার কাঠামো এখনও একটি বাস্তব গেমে কাজ করে।
এখানে আরও দুটি বিষয় রয়েছে যা আমি বিশ্বাস করি না যে এখনও আলোচনায় উঠে এসেছিল।
অলসতা সমসাময়িক পরিবেশে একটি সুসংগত প্রক্রিয়া। কিছু গণনার রেফারেন্স তৈরি করা এবং এটির ফলাফলগুলি অনেক থ্রেডের মধ্যে ভাগ করে নেওয়ার এটি একটি হালকা ও সহজ উপায়। যদি একাধিক থ্রেড অপ্রয়োজনীয় মানটি অ্যাক্সেস করার চেষ্টা করে তবে তার মধ্যে কেবল একটিই এটি কার্যকর করবে এবং অন্যগুলি সেই অনুযায়ী এটি উপলব্ধ হয়ে গেলে এটি প্রাপ্ত হবে।
খাঁটি সেটিং-এ ডেটা স্ট্রাকচারকে এমর্টাইজ করার জন্য অলসতা মৌলিক। এটি ওকাসাকী বিশুদ্ধভাবে কার্যকরী ডেটা স্ট্রাকচারগুলিতে বিশদভাবে বর্ণনা করেছেন , তবে মূল ধারণাটি অলস মূল্যায়ন হ'ল মিউটেশনটির একটি নিয়ন্ত্রিত রূপ যা আমাদের নির্দিষ্ট ধরণের ডেটা স্ট্রাকচারকে দক্ষতার সাথে প্রয়োগ করতে দেয়। যদিও আমরা প্রায়শই অলসতার কথা বলি যা আমাদেরকে বিশুদ্ধ চুলের শার্ট পরতে বাধ্য করে, অন্যভাবে এটি প্রযোজ্য: এগুলি সিনেরজিস্টিক ভাষার বৈশিষ্ট্যগুলির একটি জুড়ি।
আপনি যখন কম্পিউটার চালু করেন এবং উইন্ডোজ আপনার উইন্ডোজ এক্সপ্লোরারে আপনার হার্ড ড্রাইভে প্রতিটি ডিরেক্টরি খোলার থেকে বিরত থাকে এবং আপনার কম্পিউটারে ইনস্টলিত প্রতিটি একক প্রোগ্রাম চালু করা থেকে বিরত থাকে, যতক্ষণ না আপনি নির্দিষ্ট নির্দেশিকা প্রয়োজন বা নির্দিষ্ট প্রোগ্রামের প্রয়োজন হয় তা নির্দেশ না করে, "অলস" মূল্যায়ন হয়।
"অলস" মূল্যায়ন অপারেশন সম্পাদন করে যখন তাদের প্রয়োজন হয়। এটি যখন প্রোগ্রামিং ভাষা বা গ্রন্থাগারের বৈশিষ্ট্য হয় তখন এটি কার্যকর কারণ সাধারণভাবে সামনের সবকিছুকে প্রাক্কলিত করার চেয়ে আপনার নিজের পক্ষে অলস মূল্যায়ন কার্যকর করা সাধারণত কঠিন।
এই বিবেচনা:
if (conditionOne && conditionTwo) {
doSomething();
}
কন্ডিশনটি সত্য এবং শর্ত দুটি সত্য হলেই ডসোমিংথিং () পদ্ধতিটি কার্যকর করা হবে । এক্ষেত্রে যেখানে কন্ডিশন একটি মিথ্যা, আপনার শর্তের ফলাফল দুটি গণনা করার দরকার কেন? শর্তের মূল্যায়ন দুই ক্ষেত্রে এই ক্ষেত্রে অপচয় হবে, বিশেষত যদি আপনার অবস্থাটি কোনও পদ্ধতি প্রক্রিয়ার ফলাফল।
অলস মূল্যায়নের আগ্রহের এটি একটি উদাহরণ ...
এটি দক্ষতা বৃদ্ধি করতে পারে। এটি হ'ল সুস্পষ্ট চেহারা, তবে এটি আসলে সবচেয়ে গুরুত্বপূর্ণ নয়। (নোট এছাড়াও আলস্য যে পারেন বধ - অস্থায়ী ফলাফল প্রচুর আপ সংরক্ষণ বদলে তাদের অবিলম্বে গণনা করে এই সত্য অবিলম্বে সুস্পষ্ট নয় তবে, আপনি র্যাম একটি বিশাল পরিমাণ ব্যবহার করতে পারেন দক্ষতা খুব।।)
এটি আপনাকে ভাষায় হার্ড-কোডেড না হয়ে সাধারণ ব্যবহারকারী-স্তরের কোডে ফ্লো কন্ট্রাক্ট কনস্ট্রাক্টগুলি সংজ্ঞায়িত করতে দেয়। (উদাহরণস্বরূপ, জাভার for
লুপ রয়েছে; হাস্কেলের একটি for
ফাংশন রয়েছে exception জাভা এর ব্যতিক্রম হ্যান্ডলিং রয়েছে; হাস্কেলের রয়েছে বিভিন্ন ধরণের ব্যতিক্রম মোনাড C সি # আছে goto
; হাস্কেলের ধারাবাহিকতা মোনাদ রয়েছে ...)
এটা আপনার জন্য অ্যালগরিদম decouple দেয় উৎপাদিত সিদ্ধান্ত নেওয়ার জন্য অ্যালগরিদম থেকে ডেটা কত জেনারেট করতে ডেটা। আপনি এমন একটি ফাংশন লিখতে পারেন যা ফলাফলের একটি ধারণা-অসীম তালিকা তৈরি করে এবং অন্য একটি ফাংশন যা এই তালিকার যতটা প্রক্রিয়া প্রয়োজন তত সিদ্ধান্ত নেয়। আরও উল্লেখযোগ্যভাবে, আপনার পাঁচটি জেনারেটর ফাংশন এবং পাঁচটি গ্রাহক ফাংশন থাকতে পারে এবং আপনি দক্ষতার সাথে কোনও সংমিশ্রণ তৈরি করতে পারেন - 5 x 5 = 25 ফাংশনটি ম্যানুয়ালি কোডিংয়ের পরিবর্তে একবারে উভয় ক্রিয়াকে একত্রিত করে। (!) আমরা সকলেই জানি ডিকোপল করা ভাল জিনিস।
এটি কম-বেশি আপনাকে খাঁটি কার্যকরী ভাষা ডিজাইন করতে বাধ্য করে। এটা সবসময় শর্টকাট নিতে প্রলুব্ধ, কিন্তু একটি অলস ভাষায়, নামমাত্র অপবিত্রতা আপনার কোড তোলে দুর্দান্তভাবে অনিশ্চিত, শর্টকাট গ্রহণ বিরুদ্ধে যা দৃঢ়ভাবে militates।
অলসতার একটি বিশাল সুবিধা হ'ল যুক্তিশীল সীমানা সহ অবিচ্ছেদ্য ডেটা কাঠামো লেখার ক্ষমতা। একটি সাধারণ উদাহরণ হ'ল অপরিবর্তনীয় স্ট্যাক (এফ # ব্যবহার করে):
type 'a stack =
| EmptyStack
| StackNode of 'a * 'a stack
let rec append x y =
match x with
| EmptyStack -> y
| StackNode(hd, tl) -> StackNode(hd, append tl y)
কোডটি যুক্তিসঙ্গত, তবে দুটি স্ট্যাক যোগ করার জন্য x এবং y সবচেয়ে ভাল, সবচেয়ে খারাপ এবং গড় ক্ষেত্রে ও (এক্স দৈর্ঘ্য) সময় নেয়। দুটি স্ট্যাক যুক্ত করা একতরফা ক্রিয়া, এটি স্ট্যাক এক্সের সমস্ত নোডকে স্পর্শ করে।
আমরা অলস স্ট্যাক হিসাবে ডেটা স্ট্রাকচারটি আবার লিখতে পারি:
type 'a lazyStack =
| StackNode of Lazy<'a * 'a lazyStack>
| EmptyStack
let rec append x y =
match x with
| StackNode(item) -> Node(lazy(let hd, tl = item.Force(); hd, append tl y))
| Empty -> y
lazy
এর নির্মাতায় কোডের মূল্যায়ন স্থগিত করে কাজ করে। একবার ব্যবহার করে মূল্যায়ন করা হলে .Force()
, ফেরতের মান প্রতিটি পরবর্তী সময়ে ক্যাশে হয় এবং পুনরায় ব্যবহৃত হয় .Force()
।
অলস সংস্করণ সহ, সংযোজনগুলি একটি ও (1) অপারেশন: এটি 1 টি নোড দেয় এবং তালিকার প্রকৃত পুনর্নির্মাণ স্থগিত করে। আপনি যখন এই তালিকার শীর্ষস্থানীয় পাবেন, এটি নোডের বিষয়বস্তুগুলি মূল্যায়ন করবে, বাধ্য করে এটি মাথা ফেরা করে এবং অবশিষ্ট উপাদানগুলির সাথে একটি সাসপেনশন তৈরি করে, সুতরাং তালিকার শীর্ষস্থান গ্রহণ করা একটি হে (1) অপারেশন।
সুতরাং, আমাদের অলস তালিকাটি পুনর্নির্মাণের একটি স্থির অবস্থায় রয়েছে, আপনি যতক্ষণ না এই তালিকাটির সমস্ত উপাদানগুলির মধ্যে প্রবেশ করেন ততক্ষণ আপনি এই তালিকাটি পুনর্নির্মাণের জন্য অর্থ প্রদান করবেন না। অলসতা ব্যবহার করে, এই তালিকা ও (1) কনসিং এবং সংযোজন সমর্থন করে। মজার বিষয় হল যেহেতু আমরা নোডগুলি অ্যাক্সেস না করা পর্যন্ত মূল্যায়ন করি না, তাই সম্ভাব্য অসীম উপাদানগুলির সাথে একটি তালিকা তৈরি করা সম্পূর্ণভাবে সম্ভব।
উপরের ডেটা স্ট্রাকচারের জন্য প্রতিটি ট্র্যাভার্সালগুলিতে নোডগুলি পুনরায় সংশোধন করার প্রয়োজন হয় না, সুতরাং সেগুলি নেট .NET এ ভ্যানিলা আইইনামেবলগুলির থেকে আলাদা।
এই স্নিপেট অলস এবং অলস মূল্যায়নের মধ্যে পার্থক্য দেখায়। অবশ্যই এই ফিবোনাচি ফাংশনটি নিজেই অনুকূলিত হতে পারে এবং পুনরাবৃত্তির পরিবর্তে অলস মূল্যায়ন ব্যবহার করতে পারে তবে এটি উদাহরণটি নষ্ট করে দেবে।
আসুন অনুমান আমরা পারে সব 20 নম্বর আপফ্রন্ট উত্পন্ন করা হবে না অলস মূল্যায়ন সঙ্গে, কিছু জন্য 20 প্রথম সংখ্যা ব্যবহার করতে হবে, কিন্তু, অলস মূল্যায়ন সঙ্গে তারা শুধুমাত্র প্রয়োজনীয় উত্পন্ন করা হবে। সুতরাং আপনি প্রয়োজন হলে কেবল গণনার মূল্য প্রদান করবেন।
নমুনা আউটপুট
অলস প্রজন্ম নয়: 0.023373 অলস প্রজন্ম: 0.000009 অলস আউটপুট নয়: 0.000921 অলস আউটপুট: 0.024205
import time
def now(): return time.time()
def fibonacci(n): #Recursion for fibonacci (not-lazy)
if n < 2:
return n
else:
return fibonacci(n-1)+fibonacci(n-2)
before1 = now()
notlazy = [fibonacci(x) for x in range(20)]
after1 = now()
before2 = now()
lazy = (fibonacci(x) for x in range(20))
after2 = now()
before3 = now()
for i in notlazy:
print i
after3 = now()
before4 = now()
for i in lazy:
print i
after4 = now()
print "Not lazy generation: %f" % (after1-before1)
print "Lazy generation: %f" % (after2-before2)
print "Not lazy output: %f" % (after3-before3)
print "Lazy output: %f" % (after4-before4)
অলস মূল্যায়ন ডেটা স্ট্রাকচারের সাথে সবচেয়ে কার্যকর। আপনি কাঠামোতে কেবল নির্দিষ্ট পয়েন্ট নির্দিষ্ট করে এবং অন্যকে পুরো অ্যারের শর্তে প্রকাশ করে একটি অ্যারে বা ভেক্টর সংজ্ঞায়িত করতে পারেন। এটি আপনাকে খুব সংক্ষিপ্তভাবে এবং উচ্চ রান-টাইম পারফরম্যান্স সহ ডেটা স্ট্রাকচার তৈরি করতে দেয়।
এটি কার্যকরভাবে দেখতে, আপনি আমার স্নায়ুবহুল নেটওয়ার্ক লাইব্রেরিতে প্রবৃত্তি নামে একবার দেখতে পারেন । এটি কমনীয়তা এবং উচ্চ কার্যকারিতার জন্য অলস মূল্যায়নের ভারী ব্যবহার করে। উদাহরণস্বরূপ আমি প্রচলিত অপরিহার্য অ্যাক্টিভেশন গণনা থেকে সম্পূর্ণ মুক্তি পেয়েছি। একটি সহজ অলস ভাবটি আমার জন্য সবকিছু করে।
এটি উদাহরণস্বরূপ অ্যাক্টিভেশন ফাংশনে এবং ব্যাকপ্রোপ্যাগেশন লার্নিং অ্যালগরিদমেও ব্যবহৃত হয় (আমি কেবল দুটি লিঙ্কই পোস্ট করতে পারি, সুতরাং আপনাকে নিজেই মডিউলটিতে learnPat
ফাংশনটি সন্ধান করতে হবে AI.Instinct.Train.Delta
)। Ditionতিহ্যগতভাবে উভয়ের জন্য আরও অনেক জটিল পুনরাবৃত্তির অ্যালগোরিদম প্রয়োজন।
অন্যান্য লোকেরা ইতিমধ্যে সমস্ত বড় কারণ দিয়েছে, তবে আমি মনে করি কেন অলসতার বিষয়টি কঠোর ভাষায় একটি নির্দিষ্ট পয়েন্ট ফাংশনটি চেষ্টা করা এবং লিখতে হয় তা বোঝার জন্য একটি দরকারী অনুশীলন ।
হাসকেলে, একটি নির্দিষ্ট পয়েন্টের কাজটি অত্যন্ত সহজ:
fix f = f (fix f)
এটি প্রসারিত হয়
f (f (f ....
তবে হাস্কেল অলস কারণ, গণনার সেই অসীম চেইন কোনও সমস্যা নয়; মূল্যায়ন "বাইরে থেকে ভিতরে" করা হয়, এবং সবকিছু আশ্চর্যজনকভাবে কাজ করে:
fact = fix $ \f n -> if n == 0 then 1 else n * f (n-1)
গুরুত্বপূর্ণভাবে, এটি fix
অলস হওয়া গুরুত্বপূর্ণ নয় , তবে এটি f
অলস হতে হবে। একবার আপনাকে ইতিমধ্যে একটি কড়া দেওয়া হয়ে গেলে আপনি f
হয় বাতাসে হাত নিক্ষেপ করতে পারেন এবং ছেড়ে দিতে পারেন, বা এটিকে প্রসারিত করতে এবং বিশৃঙ্খলা করতে পারেন। (এটি অনেকটা নোহ লাইব্রেরি হওয়ায় ভাষাটি নয় বলে এটি যা বলেছিল তার মতো )।
এখন কঠোর স্কালায় একই ফাংশনটি লেখার কল্পনা করুন:
def fix[A](f: A => A): A = f(fix(f))
val fact = fix[Int=>Int] { f => n =>
if (n == 0) 1
else n*f(n-1)
}
আপনি অবশ্যই একটি স্ট্যাক ওভারফ্লো পান। আপনি যদি এটি কাজ করতে চান তবে আপনাকে f
আর্গুমেন্টকে প্রয়োজন অনুসারে কল করতে হবে:
def fix[A](f: (=>A) => A): A = f(fix(f))
def fact1(f: =>Int=>Int) = (n: Int) =>
if (n == 0) 1
else n*f(n-1)
val fact = fix(fact1)
আপনি কীভাবে বর্তমানে জিনিসগুলি সম্পর্কে ভাবেন তা আমি জানি না, তবে ভাষা বৈশিষ্ট্যের পরিবর্তে অলস মূল্যায়নটিকে লাইব্রেরির সমস্যা হিসাবে ভাবা আমার পক্ষে দরকারী।
আমি বোঝাতে চাইছি যে কঠোর ভাষায়, আমি কয়েকটি ডেটা স্ট্রাকচার তৈরি করে অলস মূল্যায়ন বাস্তবায়ন করতে পারি এবং অলস ভাষায় (কমপক্ষে হাস্কেল) আমি যখন চাই তখন কঠোরতার জন্য জিজ্ঞাসা করতে পারি। অতএব, ভাষার পছন্দটি আপনার প্রোগ্রামগুলিকে সত্যই অলস বা অলস করে তোলে না, তবে আপনি ডিফল্টরূপে যা পান তা কেবল তা প্রভাবিত করে।
একবার আপনি এটির মতো চিন্তাভাবনা করে, তারপরে আপনি যে কোনও ডেটা কাঠামো লিখেছেন সেই জায়গাগুলির কথা চিন্তা করুন যা আপনি পরে ডেটা তৈরি করতে ব্যবহার করতে পারেন (তার আগে এর আগে খুব বেশি তাকানো না) এবং আপনি অলসতার জন্য প্রচুর ব্যবহার দেখতে পাবেন মূল্যায়ন।
অলস মূল্যায়নের সবচেয়ে দরকারী শোষণ যা আমি ব্যবহার করেছি তা হ'ল একটি ক্রিয়া যা একটি নির্দিষ্ট ক্রমে একটি সিরিজ সাব-ফাংশন বলে। যদি এই সাব-ফাংশনগুলির মধ্যে কোনওটি ব্যর্থ হয় (মিথ্যা প্রত্যাবর্তিত হয়), কলিং ফাংশনটি অবিলম্বে ফিরে আসতে হবে। সুতরাং আমি এটি এইভাবে করতে পারতাম:
bool Function(void) {
if (!SubFunction1())
return false;
if (!SubFunction2())
return false;
if (!SubFunction3())
return false;
(etc)
return true;
}
বা, আরও মার্জিত সমাধান:
bool Function(void) {
if (!SubFunction1() || !SubFunction2() || !SubFunction3() || (etc) )
return false;
return true;
}
আপনি এটি ব্যবহার শুরু করার পরে, আপনি এটি আরও এবং আরও প্রায়ই ব্যবহার করার সুযোগ দেখতে পাবেন।
অলস মূল্যায়ন ছাড়া আপনাকে এ জাতীয় কিছু লেখার অনুমতি দেওয়া হবে না:
if( obj != null && obj.Value == correctValue )
{
// do smth
}
অন্যান্য জিনিসগুলির মধ্যে, অলস ভাষাগুলি বহুমাত্রিক অসীম ডেটা স্ট্রাকচারের অনুমতি দেয়।
স্কিম, পাইথন ইত্যাদি স্ট্রিম সহ একক মাত্রিক অসীম ডেটা স্ট্রাকচারের অনুমতি দেয়, আপনি কেবলমাত্র একটি মাত্রা ধরে যেতে পারেন can
অলসতা একই ফ্রঞ্জ সমস্যার জন্য দরকারী তবে সেই লিঙ্কে উল্লিখিত কর্টাইন সংযোগটি লক্ষ্য করার মতো।
অলস মূল্যায়ন হ'ল দরিদ্রের সমীকরণীয় যুক্তি (যা প্রত্যাশা করা যেতে পারে, আদর্শভাবে, জড়িত প্রকারের এবং ক্রিয়াকলাপের সম্পত্তি থেকে কোডের বৈশিষ্ট্যগুলি হ্রাস করা)।
উদাহরণ যেখানে বেশ ভাল এটা কাজ করে: sum . take 10 $ [1..10000000000]
। যা আমরা কেবল একটি প্রত্যক্ষ এবং সাধারণ সংখ্যার গণনার পরিবর্তে 10 সংখ্যায় যোগ করাতে আপত্তি করি না। অবশ্যই অলস মূল্যায়ন ছাড়াই এটি কেবল প্রথম 10 টি উপাদান ব্যবহার করার জন্য মেমোরিতে একটি বিশাল তালিকা তৈরি করবে। এটি অবশ্যই খুব ধীর হবে এবং স্মৃতি-বহির্ভূত ত্রুটির কারণ হতে পারে।
উদাহরণ যেখানে এটি মহান হিসাবে নয় হিসাবে আমরা চাই: sum . take 1000000 . drop 500 $ cycle [1..20]
। যা আসলে তালিকার পরিবর্তে লুপে থাকলেও 1 000 000 সংখ্যাগুলি যোগ করবে; এখনও এটি করা উচিত নয় শুধু একটা সরাসরি সাংখ্যিক হিসাব কমে যাবে, কয়েক কন্ডিশন ও কয়েক সূত্র সঙ্গে। কোনটি হবে অনেক বেশি ভালো তারপর 1 000 000 নম্বর summing হও। এমনকি যদি কোনও লুপে থাকে এবং কোনও তালিকায় না থাকে (যেমন বনভূমি অপ্টিমাইজেশনের পরে)।
আরেকটি বিষয় হ'ল এটি পুচ্ছ পুনরাবৃত্তি মডুলো কনস স্টাইলে কোড করা সম্ভব করে এবং এটি কেবল কার্যকর ।
cf. সম্পর্কিত উত্তর ।
যদি "অলস মূল্যায়ন" দ্বারা বোঝানো হয় তবে আপনি কম্বাউন্ড বুলিয়ানগুলির মতো পছন্দ করুন in
if (ConditionA && ConditionB) ...
তারপরে উত্তরটি কেবল হ'ল প্রোগ্রামটি যত কম সিপিইউ চক্র গ্রহণ করবে, তত দ্রুত এটি চালিত হবে ... এবং যদি প্রসেসিংয়ের নির্দেশাবলীর একটি অংশ যদি প্রোগ্রামের ফলাফলের উপর কোনও প্রভাব ফেলবে না তবে তা অপ্রয়োজনীয়, (এবং অতএব অপচয়) সময়ের সাথে সাথে) সেগুলি সম্পাদন করার জন্য ...
যদি ওটোহ হয়, তবে আপনি যা বোঝাতে চেয়েছিলেন আমি "অলস সূচনা" হিসাবে এই হিসাবে লিখেছি:
class Employee
{
private int supervisorId;
private Employee supervisor;
public Employee(int employeeId)
{
// code to call database and fetch employee record, and
// populate all private data fields, EXCEPT supervisor
}
public Employee Supervisor
{
get
{
return supervisor?? (supervisor = new Employee(supervisorId));
}
}
}
ভাল, এই কৌশলটি ক্লায়েন্ট কোড ব্যবহার করে সুপারভাইজার ডেটা রেকর্ডের জন্য ডাটাবেস কল করার প্রয়োজন এড়াতে ক্লায়েন্ট কোডকে অনুমতি দেয় কেবলমাত্র যখন কর্মচারী অবজেক্ট ব্যবহার করে ক্লায়েন্টকে তদারকির ডেটা অ্যাক্সেসের প্রয়োজন হয় ... এটি কোনও কর্মচারীর তাত্ক্ষণিক প্রক্রিয়াটি দ্রুত করে তোলে, এবং তবুও যখন আপনার সুপারভাইজারের প্রয়োজন হবে তখন সুপারভাইজার সম্পত্তিটিতে প্রথম কলটি ডেটাবেস কলটি ট্রিগার করবে এবং ডেটা আনবে এবং উপলভ্য হবে ...
থেকে উদ্ধৃতাংশ উচ্চ অর্ডার ফাংশন
38,000 দ্বারা বিভাজ্য 100,000 এর অধীনে বৃহত্তম সংখ্যাটি সন্ধান করি that এটি করার জন্য, আমরা কেবলমাত্র সম্ভাবনার একটি সেট ফিল্টার করব যেখানে আমরা সমাধানটি জানি know
largestDivisible :: (Integral a) => a
largestDivisible = head (filter p [100000,99999..])
where p x = x `mod` 3829 == 0
আমরা প্রথমে 100,000 এর চেয়ে কম সংখ্যক সমস্ত সংখ্যার একটি তালিকা তৈরি করি। তারপরে আমরা এটিকে আমাদের ভবিষ্যদ্বাণী করে ফিল্টার করি এবং কারণ সংখ্যাগুলি একটি উত্থিত পদ্ধতিতে বাছাই করা হয়, বৃহত্তম সংখ্যক যা আমাদের ভবিষ্যদ্বাণীকে সন্তুষ্ট করে তা ফিল্টার তালিকার প্রথম উপাদান। এমনকি আমাদের প্রারম্ভিক সেটটির জন্য একটি সীমাবদ্ধ তালিকা ব্যবহার করার দরকার পড়েনি। আবার অ্যাকশনে অলসতা। যেহেতু আমরা কেবল ফিল্টারড তালিকার শীর্ষস্থানীয় ব্যবহার করেই শেষ করি, ফিল্টারযুক্ত তালিকা সীমাবদ্ধ বা অসীম কিনা তা বিবেচ্য নয়। প্রথম পর্যাপ্ত সমাধান পাওয়া গেলে মূল্যায়ন বন্ধ হয়ে যায়।