我试图很好地掌握状态Monad (以及一般的Monad),但是我很难用状态Monad和do-表示法重写下面的函数,这是我提出here的一个练习。
import Control.Monad
import System.Random
import Data.Complex
import qualified System.Random as R
import Control.Monad.Trans.State.Lazy
giveRandomElement :: [a] -> State R.StdGen a
giveRandomElement lst = do
let n = length lst
rand <- state $ randomR (0, n-1)
return $ lst !! rand
random_response_monad :: a -> [a] -> State R.StdGen a
random_response_monad true_answer answers = do
tal <- state $ randomR (0, 1) :: StateT StdGen Data.Functor.Identity.Identity a
if (tal == 0) then true_answer
else giveRandomElement answers显而易见,tal-variable存在一些类型问题,因为它发生在if-clause和do-expression的第一行中。正如从代码中可以看到的那样,我试图通过特定类型强制后者,以便使它对我自己来说也是明确和清晰的。我这样做是通过编译器--当我第一次尝试强制它成为Int-type时,我得到了这样的建议。但是,我无法在if-语句中使用该值,我也不知道如何转换或解压缩该值,以便将其作为Int。到目前为止,我已经尝试在tal <- ...,resp <- get $ tal之后添加折叠行,但是我得到了这个输出。
error:
* Couldn't match expected type: t0
-> StateT StdGen Data.Functor.Identity.Identity a1
with actual type: StateT s0 m0 s0
* The first argument of ($) takes one value argument,
but its type `StateT s0 m0 s0' has none
In a stmt of a 'do' block: resp <- get $ tal
In the expression:
do tal <- state $ randomR (0, 1)
resp <- get $ tal
if (resp == 0) then
giveRandomElement answers
else
giveRandomElement answers
* Relevant bindings include tal :: t0 此外,我感到困惑的是,什么是“打印”giveRandomElement返回的结果的最佳方法,因为该类型基于为State-monad声明的类型,据我所知,该类型也不使用deriving Show。但是,这也许可以通过解压上面询问的值来解决。
编辑
我使用了上面的包,尽管它们可能并不都在上面的代码中使用。我不确定代码使用的是哪种代码,我怀疑qualified System.Random as R
发布于 2022-11-22 13:47:30
以下代码行:
tal <- state $ randomR (0, 1) :: StateT StdGen Data.Functor.Identity.Identity a 是相当长的,可能会导致水平滑块出现,至少在我的平台上。
因此,很容易忽略的是,在它的末尾使用a类型变量,而它应该是Int。
此外,if结构的两个分支使用不同的类型,从而使构造类型错误。then分支提供一个纯a值,而else分支提供一个一元值。这很容易通过更改为:
if (tal == 0) then return true_answer由于return库函数(稍微命名不当)将其参数封装到手头的monad中。
下面的代码试图保持代码行的足够短,似乎工作得很好:
import Control.Monad.State
import qualified System.Random as R
import qualified Data.Functor.Identity as DFI
giveRandomElement :: [a] -> State R.StdGen a
giveRandomElement lst = do
let n = length lst
rand <- state $ R.randomR (0, n-1)
return $ lst !! rand
type ActionType = StateT R.StdGen DFI.Identity Int
random_response_monad :: a -> [a] -> State R.StdGen a
random_response_monad true_answer answers = do
tal <- (state $ R.randomR (0, 1) :: ActionType)
if (tal == 0) then return true_answer
else giveRandomElement answers
main :: IO ()
main = do
let g0 = R.mkStdGen 4243
action = random_response_monad 20 [0..9]
(k, g1) = runState action g0
putStrLn $ "k is set to: " ++ (show k)附带注意:代码也可以在没有复杂类型注释的情况下编译,如下所示:
tal <- state $ R.randomR (0::Int, 1)发布于 2022-11-30 21:17:46
像这样的事情似乎很有效:
random_response_monad :: a -> [a] -> State R.StdGen a
random_response_monad true_answer answers = do
tal <- state $ randomR (0 :: Int, 1)
if (tal == 0) then return true_answer
else giveRandomElement answers两个变化:
0和1是什么意思。一旦您告诉编译器0是哪种类型,那么1具有相同的类型。(请记住,在Haskell中,数字是多态的。如果没有更多的信息,Haskell将在true_answer.前面看到像0这样的文字可能是任何Num Num。
以下是来自GHCi的几个示例,它们似乎表明了它的工作原理:
ghci> evalState (random_response_monad 42 [0..9]) <$> newStdGen
4
ghci> evalState (random_response_monad 42 [0..9]) <$> newStdGen
1
ghci> evalState (random_response_monad 42 [0..9]) <$> newStdGen
42https://stackoverflow.com/questions/74531622
复制相似问题