كيف يمكنني تهيئة الدولة بطريقة مخفية في Haskell (مثل PRNG)؟

StackOverflow https://stackoverflow.com/questions/1121340

  •  13-09-2019
  •  | 
  •  

سؤال

ذهبت من خلال بعض البرامج التعليمية على الدولة مناد وأعتقد أنني حصلت على الفكرة.

على سبيل المثال، كما هو الحال في هذا البرنامج التعليمي لطيفة:

import Data.Word

type LCGState = Word32

lcg :: LCGState -> (Integer, LCGState)
lcg s0 = (output, s1) 
  where s1 = 1103515245 * s0 + 12345
        output = fromIntegral s1 * 2^16 `div` 2^32


getRandom :: State LCGState Integer
getRandom = get >>= \s0 -> let (x,s1) = lcg s0
                           in put s1 >> return x

حسنا، حتى أتمكن من استخدام getrandom:

*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 1              
(16838,1103527590)

ولكن ما زلت بحاجة إلى اجتياز البذور إلى PRNG في كل مرة أسميها. أعلم أن PRNG المتاحة في تطبيقات Haskell لا تحتاج إلى أن:

Prelude> :module Random
Prelude Random> randomRIO (1,6 :: Int)
(...) -- GHC prints some stuff here
6
Prelude Random> randomRIO (1,6 :: Int)
1

لذلك ربما أسيء فهم الدولة مناد الدولة، لأن ما استطعت رؤيته في معظم البرامج التعليمية لا يبدو أن حالة "مستمرة"، ولكن مجرد وسيلة مريحة لدولة الموضوع.

لذلك ... كيف يمكنني الحصول على حالة تتم تهيئتها تلقائيا (ممكن من بعض الوظائف التي تستخدم الوقت وغيرها من البيانات غير القابلة للتنبؤ بها)، مثل الوحدة العشوائية؟

شكرا جزيلا!

هل كانت مفيدة؟

المحلول

randomRIO يستخدم IO مناد. يبدو أن هذا يعمل بشكل جيد في مترجم شفوي لأن المترجم يعمل أيضا في IO مناد. هذا ما تراه في مثالك؛ لا يمكنك فعلا القيام بذلك في المستوى الأعلى في التعليمات البرمجية - سيتعين عليك وضعه في التعبيرات مثل جميع الموناد على أي حال.

في الكود العام، يجب عليك تجنب IO Monad، لأنه بمجرد استخدام الرمز الخاص بك IO Monad، يتم ربطه بالحالة الخارجية إلى الأبد - لا يمكنك الخروج منه (أي إذا كان لديك رمز يستخدم IO Monad، أي رمز هذا يدعو أيضا إلى استخدام IO Monad؛ لا توجد طريقة آمنة ل "الخروج" منها). لذلك IO يجب أن تستخدم مناد فقط لأشياء مثل الوصول إلى البيئة الخارجية، والأشياء التي تكون مطلوبة تماما.

بالنسبة للأشياء مثل الحالة المحلية المحفوظة، يجب ألا تستخدم IO Monad. يمكنك استعمال ال State مناد كما ذكرت، أو يمكنك استخدام ST مناد. يحتوي سانت موناد على الكثير من الميزات نفسها مثل IO Monad؛ أي هناك STRef خلايا متغيرة، مماثلة ل IORef. وبعد والشيء الجميل حول ST مقارنة مع IO هو أنه عند الانتهاء، يمكنك الاتصال runST على سانت موناد للحصول على نتيجة الحساب من مناد، لا يمكنك القيام به مع IO.

بالنسبة إلى "إخفاء" الدولة، فإن ذلك يأتي فقط كجزء من بناء جملة التعبيرات في Haskell for Monads. إذا كنت تعتقد أنك بحاجة إلى تمرير الحالة بشكل صريح، فأنت لا تستخدم بناء جملة موناد بشكل صحيح.

هنا هو الرمز الذي يستخدم IOREF في IO Monad:

import Data.IORef
foo :: IO Int -- this is stuck in the IO monad forever
foo = do x <- newIORef 1
         modifyIORef x (+ 2)
         readIORef x
-- foo is an IO computation that returns 3

هنا هو الكود الذي يستخدم سانت موناد:

import Control.Monad.ST
import Data.STRef
bar :: Int
bar = runST (do x <- newSTRef 1
                modifySTRef x (+ 2)
                readSTRef x)
-- bar == 3

بساطة الكود هي نفسها في الأساس؛ باستثناء ذلك في الحالة الأخيرة يمكننا الحصول على القيمة من الماناد، وفي السابق لا يمكننا دون أن نضعها داخل حساب IO آخر.

نصائح أخرى

secretStateValue :: IORef SomeType
secretStateValue = unsafePerformIO $ newIORef initialState
{-# NOINLINE secretStateValue #-}

يمكنك الآن الوصول إلى SecretStateValue مع طبيعي READIORF و TREETYORF., ، في بلدي مناد.

لذلك ربما أسيء فهم الدولة مناد الدولة، لأن ما استطعت رؤيته في معظم البرامج التعليمية لا يبدو أن حالة "مستمرة"، ولكن مجرد وسيلة مريحة لدولة الموضوع.

الدولة موناد هي بالضبط حول حالة الخيوط من خلال بعض النطاق.

إذا كنت تريد حالة المستوى الأعلى، فهذا خارج اللغة (وستحصل عليك استخدام متغير قابل للتغيير قابل للتغيير). لاحظ كيف من المحتمل أن يكون هذا أمرا معقدا سلامة الخيط في الكود الخاص بك - كيف يتم تهيئة تلك الحالة؟ وعندما؟ وبهذا الموضوع؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top