Haskell: 放入 State monad 似乎被省略了
Haskell: put in State monad seems to be elided
我正在编写一个程序来为人们分配披萨;每个人都会得到一个比萨饼,最好是他们最喜欢的类型,除非库存 运行,在这种情况下,他们会递归地获得下一个最喜欢的类型。
我的方法是计算 ((User, Pizza), Int)
一个人想要披萨的数量,对它们进行排序,然后通过使用状态 monad 递归来保持库存计数。
程序编写及类型检查:
allocatePizzasImpl :: [((User, Pizza), Int)]
-> State [(Pizza, Int)] [(User, Pizza)]
allocatePizzasImpl [] = return []
allocatePizzasImpl ((user, (flavor, _)):ranks) =
do inventory <- get
-- this line is never hit
put $ updateWith inventory (\i -> if i <= 0
then Nothing
else Just $ i - 1) flavor
next <- allocatePizzasImpl $ filter ((/= user) . fst) ranks
return $ (user, flavor) : next
我有一个辅助函数来提取结果:
allocatePizzas :: [Pizza]
-> [((User, Pizza), Int)]
-> [(User, Pizza)]
allocatePizzas pizzas rank = fst
. runState (allocatePizzasImpl rank)
$ buildQuotas pizzas
但是 -- this line is never hit
指示的行是...从未遇到任何 GHCI 断点;此外,如果我中断 return 调用,GHCI 会说 inventory
不在范围内。
当 运行 时,结果是将相同的比萨饼(具有一个库存计数)分配给所有用户。出了点问题,但我完全不知道如何进行。我是 Haskell 的新手,所以任何关于风格的评论也将不胜感激 =)
谢谢!
PS:为了完整性,updateWith
定义为:
updateWith :: (Eq a, Eq b)
=> [(a, b)] -- inventory
-> (b -> Maybe b) -- update function; Nothing removes it
-> a -- key to update
-> [(a, b)]
updateWith set update key =
case lookup key set of
Just b -> replace set
(unwrapPair (key, update b))
(fromMaybe 0 $ elemIndex (key, b) set)
Nothing -> set
where replace :: [a] -> Maybe a -> Int -> [a]
replace [] _ _ = []
replace (_:xs) (Just val) 0 = val:xs
replace (_:xs) Nothing 0 = xs
replace (x:xs) val i = x : (replace xs val $ i - 1)
unwrapPair :: Monad m => (a, m b) -> m (a, b)
unwrapPair (a, mb) = do b <- mb
return (a, b)
我认为你的函数 replace
有问题:
replace (_:xs) (Just val) 0 = val:xs
这不会注意它正在替换的值。您不是打算只替换对应于 key
的一对吗?
我想你想要
updateWith [] e k = []
updateWith ((k', v):kvs) e k
| k' == k = case e v of
Just v' -> (k, v'):kvs
Nothing -> kvs
| otherwise = (k', v) : updateWith kvs e k
问题(忽略评论者提到的其他概念性问题)原来是使用 fst
从状态中提取结果,由于某种原因不会导致实际计算状态。 运行 通过 seq
修复的结果。
不过,我很想知道为什么是这样!
编辑:正如 Daniel Wagner 在评论中指出的那样,我实际上并没有使用库存,结果证明这是真正的错误。将其标记为已接受。
我正在编写一个程序来为人们分配披萨;每个人都会得到一个比萨饼,最好是他们最喜欢的类型,除非库存 运行,在这种情况下,他们会递归地获得下一个最喜欢的类型。
我的方法是计算 ((User, Pizza), Int)
一个人想要披萨的数量,对它们进行排序,然后通过使用状态 monad 递归来保持库存计数。
程序编写及类型检查:
allocatePizzasImpl :: [((User, Pizza), Int)]
-> State [(Pizza, Int)] [(User, Pizza)]
allocatePizzasImpl [] = return []
allocatePizzasImpl ((user, (flavor, _)):ranks) =
do inventory <- get
-- this line is never hit
put $ updateWith inventory (\i -> if i <= 0
then Nothing
else Just $ i - 1) flavor
next <- allocatePizzasImpl $ filter ((/= user) . fst) ranks
return $ (user, flavor) : next
我有一个辅助函数来提取结果:
allocatePizzas :: [Pizza]
-> [((User, Pizza), Int)]
-> [(User, Pizza)]
allocatePizzas pizzas rank = fst
. runState (allocatePizzasImpl rank)
$ buildQuotas pizzas
但是 -- this line is never hit
指示的行是...从未遇到任何 GHCI 断点;此外,如果我中断 return 调用,GHCI 会说 inventory
不在范围内。
当 运行 时,结果是将相同的比萨饼(具有一个库存计数)分配给所有用户。出了点问题,但我完全不知道如何进行。我是 Haskell 的新手,所以任何关于风格的评论也将不胜感激 =)
谢谢!
PS:为了完整性,updateWith
定义为:
updateWith :: (Eq a, Eq b)
=> [(a, b)] -- inventory
-> (b -> Maybe b) -- update function; Nothing removes it
-> a -- key to update
-> [(a, b)]
updateWith set update key =
case lookup key set of
Just b -> replace set
(unwrapPair (key, update b))
(fromMaybe 0 $ elemIndex (key, b) set)
Nothing -> set
where replace :: [a] -> Maybe a -> Int -> [a]
replace [] _ _ = []
replace (_:xs) (Just val) 0 = val:xs
replace (_:xs) Nothing 0 = xs
replace (x:xs) val i = x : (replace xs val $ i - 1)
unwrapPair :: Monad m => (a, m b) -> m (a, b)
unwrapPair (a, mb) = do b <- mb
return (a, b)
我认为你的函数 replace
有问题:
replace (_:xs) (Just val) 0 = val:xs
这不会注意它正在替换的值。您不是打算只替换对应于 key
的一对吗?
我想你想要
updateWith [] e k = []
updateWith ((k', v):kvs) e k
| k' == k = case e v of
Just v' -> (k, v'):kvs
Nothing -> kvs
| otherwise = (k', v) : updateWith kvs e k
问题(忽略评论者提到的其他概念性问题)原来是使用 fst
从状态中提取结果,由于某种原因不会导致实际计算状态。 运行 通过 seq
修复的结果。
不过,我很想知道为什么是这样!
编辑:正如 Daniel Wagner 在评论中指出的那样,我实际上并没有使用库存,结果证明这是真正的错误。将其标记为已接受。