我在处理这段代码中的函数共识时遇到了问题。共识的递归定义是返回 [Action] 而不是 IO [Action]。
我是 Haskell 的新手,不明白为什么会这样。我的印象是不可能从返回值中删除 IO。
import System.Random (randomRIO)
import Data.Ord (comparing)
import Data.List (group, sort, maximumBy)
data Action = A | B deriving (Show, Eq, Ord)
-- Sometimes returns a random action
semiRandomAction :: Bool -> Action -> IO (Action)
semiRandomAction True a = return a
semiRandomAction False _ = do
x <- randomRIO (0, 1) :: IO Int
return $ if x == 0 then A else B
-- Creates a sublist for each a_i in ls where sublist i does not contain a_i
oneOutSublists :: [a] -> [[a]]
oneOutSublists [] = []
oneOutSublists (x:xs) = xs : map (x : ) (oneOutSublists xs)
-- Returns the most common element in a list
mostCommon :: (Ord a) => [a] -> a
mostCommon = head . maximumBy (comparing length) . group . sort
-- The function in question
consensus :: [Bool] -> [Action] -> IO [Action]
consensus [x] [action] = sequence [semiRandomAction x action]
consensus xs actions = do
let xs' = oneOutSublists xs
actions' = map (replicate $ length xs') actions
replies <- mapM (uncurry $ consensus) (zip xs' actions')
map mostCommon replies -- < The problem line
main = do
let xs = [True, False, False]
actions = [A, A, A]
result <- consensus xs actions
print result
ghc输出
➜ ~ stack ghc example.hs
[1 of 1] Compiling Main ( example.hs, example.o )
example.hs:29:3: error:
• Couldn't match type ‘[]’ with ‘IO’
Expected type: IO [Action]
Actual type: [Action]
• In a stmt of a 'do' block: map mostCommon replies
In the expression:
do let xs' = oneOutSublists xs
actions' = map (replicate $ length xs') actions
replies <- mapM (uncurry $ consensus) (zip xs' actions')
map mostCommon replies
In an equation for ‘consensus’:
consensus xs actions
= do let xs' = ...
....
replies <- mapM (uncurry $ consensus) (zip xs' actions')
map mostCommon replies
|
29 | map mostCommon replies
|
最佳答案
consensus
应该返回 IO [Action]
类型的值.这就是Expected type: IO [Action]
意味着。
然而,map mostCommon replies
是 [Action]
类型的表达式(因为 map
返回一个普通列表,没有 IO
)。
I was under the impression that it was not possible remove the IO from a return value.
的确如此,这就是您收到类型错误的原因。 “无法删除 IO”不是基本属性,它只是来自标准库中可用操作的类型。
那么我们如何解决这个问题呢?
您的值类型为 IO [[Action]]
, 即 mapM (uncurry $ consensus) (zip xs' actions')
.您要应聘mostCommon
到每个内部列表(在 IO
内的外部列表内)。
通过使用 <-
在do
block ,您可以在本地从 IO a
中“提取”值:
replies <- mapM (uncurry $ consensus) (zip xs' actions')
-- replies :: [[Action]]
通过使用 map
,可以申请mostCommon
到每个子列表:
map mostCommon replies :: [Action]
缺少的是您需要在 IO
中“重新包装”您的值制作 do
block 通过类型检查(do
block 中的每个单独语句必须具有相同的基本类型,在本例中为 IO
):
return (map mostCommon replies)
在这里return :: [Action] -> IO [Action]
(或一般情况下:return :: (Monad m) => a -> m a
)。
关于haskell - 返回类型丢失 IO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53314510/