আপনি একটি খাঁটি ফাংশন তৈরি করতে পারবেন না random
যা প্রতিবার যখন ডাকা হয় তখন এটি আলাদা ফলাফল দেয়। আসলে, আপনি এমনকি বিশুদ্ধ ফাংশনগুলি "কল" করতে পারবেন না can't আপনি সেগুলি প্রয়োগ করুন। সুতরাং আপনি কিছু মিস করছেন না, তবে এর অর্থ এই নয় যে এলোমেলো সংখ্যাগুলি কার্যকরী প্রোগ্রামিংয়ের বাইরে সীমাবদ্ধ। আমাকে প্রদর্শনের অনুমতি দিন, আমি জুড়ে হাসেল সিনট্যাক্স ব্যবহার করব।
একটি অত্যাবশ্যকীয় পটভূমি থেকে আগত, আপনি প্রাথমিকভাবে এ জাতীয় ধরণের র্যান্ডম আশা করতে পারেন:
random :: () -> Integer
তবে এটি ইতিমধ্যে উড়িয়ে দেওয়া হয়েছে কারণ এলোমেলোভাবে খাঁটি ফাংশন হতে পারে না।
একটি মান ধারণা বিবেচনা করুন। একটি মান একটি অপরিবর্তনীয় জিনিস। এটি কখনই পরিবর্তিত হয় না এবং আপনি যে এটি পর্যবেক্ষণ করতে পারেন তা সর্বকালের জন্য সামঞ্জস্যপূর্ণ।
স্পষ্টতই, এলোমেলোভাবে একটি পূর্ণসংখ্যা মান উত্পাদন করতে পারে না। পরিবর্তে, এটি একটি পূর্ণসংখ্যার এলোমেলো পরিবর্তনশীল উত্পাদন করে। এটি টাইপটি এর মতো দেখতে পারে:
random :: () -> Random Integer
একটি যুক্তি পাস করা সম্পূর্ণ অপ্রয়োজনীয় ব্যতীত ফাংশনগুলি খাঁটি, তাই একজনের random ()
পক্ষে অন্যটির মতোই ভাল random ()
। আমি এখান থেকে এ ধরণের এলোমেলোভাবে দেব:
random :: Random Integer
যা সব ঠিকঠাক, তবে খুব কার্যকর নয়। আপনি যেমন এক্সপ্রেশন লিখতে সক্ষম হতে পারে আশা করতে পারেন random + 42
, কিন্তু আপনি পারবেন না, কারণ এটি typecheck হবে না। আপনি এলোমেলো ভেরিয়েবলের সাহায্যে কিছুই করতে পারবেন না।
এটি একটি আকর্ষণীয় প্রশ্ন উত্থাপন। এলোমেলো ভেরিয়েবলগুলি পরিচালনা করতে কোন ফাংশন উপস্থিত থাকতে হবে?
এই ফাংশনটি থাকতে পারে না:
bad :: Random a -> a
যে কোনও কার্যকর উপায়ে, কারণ তখন আপনি লিখতে পারেন:
badRandom :: Integer
badRandom = bad random
যা একটি অসঙ্গতি পরিচয় করিয়ে দেয়। ব্যাডর্যান্ডম একটি মান হিসাবে অনুমিত হয়, তবে এটি একটি এলোমেলো সংখ্যাও; একটি দ্বন্দ্ব।
হতে পারে আমাদের এই ফাংশনটি যুক্ত করা উচিত:
randomAdd :: Integer -> Random Integer -> Random Integer
তবে এটি আরও সাধারণ প্যাটার্নের কেবল একটি বিশেষ ক্ষেত্রে। এ জাতীয় অন্যান্য এলোমেলো জিনিস পেতে আপনার এলোমেলোভাবে কোনও ক্রিয়াকলাপ প্রয়োগ করতে সক্ষম হওয়া উচিত:
randomMap :: (a -> b) -> Random a -> Random b
লেখার পরিবর্তে random + 42
, আমরা এখন লিখতে পারি randomMap (+42) random
।
আপনার সমস্ত কিছু যদি এলোমেলো ম্যাপ হয় তবে আপনি এলোমেলো ভেরিয়েবলগুলি একত্রিত করতে পারবেন না able আপনি উদাহরণস্বরূপ এই ফাংশনটি লিখতে পারেন নি:
randomCombine :: Random a -> Random b -> Random (a, b)
আপনি এটি লিখতে চেষ্টা করতে পারেন:
randomCombine a b = randomMap (\a' -> randomMap (\b' -> (a', b')) b) a
তবে এটির ভুল প্রকার রয়েছে। একটি দিয়ে শেষ হওয়ার পরিবর্তে Random (a, b)
আমরা একটি দিয়ে শেষ করিRandom (Random (a, b))
এটি অন্য ফাংশন যুক্ত করে স্থির করা যেতে পারে:
randomJoin :: Random (Random a) -> Random a
তবে, যে কারণে শেষ পর্যন্ত স্পষ্ট হয়ে উঠতে পারে, আমি তা করতে যাচ্ছি না। পরিবর্তে আমি এটি যুক্ত করতে যাচ্ছি:
randomBind :: Random a -> (a -> Random b) -> Random b
এটি তাত্ক্ষণিকভাবে সুস্পষ্ট নয় যে এটি আসলে সমস্যাটি সমাধান করে, তবে তা করে:
randomCombine a b = randomBind a (\a' -> randomMap (\b' -> (a', b')) b)
আসলে, র্যান্ডমজাইন এবং র্যান্ডমম্যাপের ক্ষেত্রে র্যান্ডমবাইন্ড লেখা সম্ভব। র্যান্ডমবাইন্ডের নিরিখে এলোমেলোভাবে জয়েন লেখাও সম্ভব। তবে, আমি এটি অনুশীলন হিসাবে ছেড়ে দেব।
আমরা এটিকে কিছুটা সহজ করতে পারি। আমাকে এই ফাংশনটি সংজ্ঞায়িত করার অনুমতি দিন:
randomUnit :: a -> Random a
এলোমেলোভাবে একটি মান একটি এলোমেলো ভেরিয়েবলে রূপান্তরিত করে। এর অর্থ হ'ল আমাদের এলোমেলো ভেরিয়েবল থাকতে পারে যা আসলে এলোমেলো নয়। যদিও সর্বদা এটি ছিল; আমরা randomMap (const 4) random
আগে করতে পারে । র্যান্ডমউনিট সংজ্ঞায়িত করার কারণটি একটি ভাল ধারণা হ'ল এখন আমরা এলোমেলো মানচিত্র এবং র্যান্ডমবাইন্ডের ক্ষেত্রে র্যান্ডমম্যাপটি সংজ্ঞায়িত করতে পারি:
randomMap :: (a -> b) -> Random a -> Random b
randomMap f x = randomBind x (randomUnit . f)
ঠিক আছে, এখন আমরা কোথাও পাচ্ছি। আমাদের র্যান্ডম ভেরিয়েবল রয়েছে যা আমরা ম্যানিপুলেট করতে পারি। যাহোক:
- আমরা প্রকৃতপক্ষে এই ফাংশনগুলি কীভাবে বাস্তবায়ন করতে পারি তা স্পষ্ট নয়,
- এটা বেশ কষ্টকর।
বাস্তবায়ন
আমি ছদ্ম এলোমেলো সংখ্যাগুলি সামলবো। বাস্তব এলোমেলো সংখ্যার জন্য এই ফাংশনগুলি বাস্তবায়ন করা সম্ভব তবে ইতিমধ্যে এই উত্তরটি বেশ দীর্ঘ getting
মূলত, এটি যেভাবে কাজ করবে এটি হ'ল আমরা সর্বত্র সর্বত্র একটি বীজ মান পাস করতে যাচ্ছি। যখনই আমরা একটি নতুন এলোমেলো মান উত্পন্ন করব, আমরা একটি নতুন বীজ উত্পাদন করব। শেষে, যখন আমরা একটি এলোমেলো ভেরিয়েবল নির্মাণ সম্পন্ন করব, তখন আমরা এই ফাংশনটি ব্যবহার করে এটি থেকে নমুনা চাইব:
runRandom :: Seed -> Random a -> a
আমি এ জাতীয় র্যান্ডম টাইপটি সংজ্ঞায়িত করতে যাচ্ছি:
data Random a = Random (Seed -> (Seed, a))
তারপরে, আমাদের কেবল র্যান্ডমউনিট, র্যান্ডমবাইন্ড, রানর্যান্ডম এবং র্যান্ডম এর বাস্তবায়ন সরবরাহ করতে হবে যা বেশ সরল-এগিয়ে রয়েছে:
randomUnit :: a -> Random a
randomUnit x = Random (\seed -> (seed, x))
randomBind :: Random a -> (a -> Random b) -> Random b
randomBind (Random f) g =
Random (\seed ->
let (seed', x) = f seed
Random g' = g x in
g' seed')
runRandom :: Seed -> Random a -> a
runRandom seed (Random f) = (snd . f) seed
এলোমেলোভাবে, আমি ধরে নিচ্ছি যে ইতিমধ্যে ধরণের একটি ফাংশন রয়েছে:
psuedoRandom :: Seed -> (Seed, Integer)
যা ক্ষেত্রে এলোমেলো ন্যায়বিচার Random psuedoRandom
।
জিনিসগুলি কম কষ্টকর করা aking
হাস্কেলের চোখে এই ভাল লাগার মতো জিনিসগুলি তৈরি করতে সিনট্যাকটিক চিনি রয়েছে। এটিকে ড-নোটেশন বলা হয় এবং এটি ব্যবহার করার জন্য আমাদের এটি করতে হবে এলোমেলো জন্য Monad এর একটি উদাহরণ তৈরি করে।
instance Monad Random where
return = randomUnit
(>>=) = randomBind
সম্পন্ন. randomCombine
আগে থেকে এখন লেখা যেতে পারে:
randomCombine :: Random a -> Random b -> Random (a, b)
randomCombine a b = do
a' <- a
b' <- b
return (a', b')
আমি যদি এটি নিজের জন্য করে নিই তবে আমি এর থেকে আরও একধাপ এগিয়ে যেতে পারি এবং আবেদনকারীর উদাহরণ তৈরি করতাম। (যদি এর কোনও অর্থ হয় না তবে চিন্তা করবেন না)।
instance Functor Random where
fmap = liftM
instance Applicative Random where
pure = return
(<*>) = ap
তারপরে র্যান্ডমকোমাইন লেখা যেতে পারে:
randomCombine :: Random a -> Random b -> Random (a, b)
randomCombine a b = (,) <$> a <*> b
এখন আমাদের কাছে এই দৃষ্টান্তগুলি রয়েছে, আমরা >>=
র্যান্ডমবাইন্ডের পরিবর্তে, র্যান্ডমজিনের পরিবর্তে যোগদান করতে পারি, এলোমেলো ম্যাপের পরিবর্তে এফএম্যাপ করতে পারি, এলোমেলোভাবে ব্যবহারের পরিবর্তে ফিরে আসি। আমরা ফাংশনের পুরো লোডও পাই।
এটা কি মূল্য? আপনি তর্ক করতে পারেন, এই পর্যায়ে পৌঁছানো, যেখানে এলোমেলো সংখ্যার সাথে কাজ করা সম্পূর্ণরূপে ভয়াবহ নয়, বেশ কঠিন এবং দীর্ঘ-বায়ুযুক্ত ছিল। এই প্রচেষ্টার বিনিময়ে আমরা কী পেলাম?
সর্বাধিক তাত্ক্ষণিক পুরষ্কারটি হ'ল আমরা এখন দেখতে পাচ্ছি যে আমাদের প্রোগ্রামের কোন অংশগুলি এলোমেলোতার উপর নির্ভরশীল এবং কোন অংশগুলি সম্পূর্ণ নির্বিচারক। আমার অভিজ্ঞতায়, এর মতো কঠোরভাবে পৃথক করার বিষয়টি জিনিসগুলিকে সহজতর করে।
আমরা এতক্ষণ ধরে ধরে নিয়েছি যে আমরা উত্পন্ন প্রতিটি র্যান্ডম ভেরিয়েবল থেকে কেবলমাত্র একটি একক নমুনা চাই, তবে যদি এটির সন্ধান হয় যে ভবিষ্যতে আমরা বাস্তবে আরও বিতরণ দেখতে চাই, এটি তুচ্ছ। আপনি বিভিন্ন বীজের সাথে একই র্যান্ডম ভেরিয়েবলটিতে কেবল রানআরডম প্রচুর সময় ব্যবহার করতে পারেন। এটি অবশ্যই আবশ্যক ভাষায় সম্ভব, তবে এই ক্ষেত্রে আমরা নিশ্চিত হতে পারি যে আমরা যতবার অপ্রত্যাশিত আইও সম্পাদন করতে যাব না প্রতিবার আমরা এলোমেলো পরিবর্তনশীলকে নমুনা করব এবং রাষ্ট্রের সূচনা করার ক্ষেত্রে আমাদের যত্নবান হতে হবে না।