哈斯克尔:投入国家monad似乎被省略了

时间:2015-04-15 18:16:39

标签: haskell state-monad

我正在编写一个程序,为人们分配比萨饼;除非库存耗尽,否则每个人都会得到一个披萨,最好是他们喜欢的披萨,在这种情况下,他们会递归地给出下一个喜欢的类型。

我的方法是计算一个人想要披萨的数量((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断点的影响;此外,如果我打破回访,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)

2 个答案:

答案 0 :(得分:1)

我认为您的函数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

答案 1 :(得分:0)

问题(忽略评论者提到的其他概念性事实)结果是使用fst从状态中提取结果,由于某种原因不会导致实际计算国家。通过seq运行结果修复了它。

我有兴趣知道为什么这就是这种情况!

编辑:正如丹尼尔瓦格纳在评论中指出的那样,我实际上并没有使用库存,结果证明这是真正的错误。将此标记为已接受。