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 在评论中指出的那样,我实际上并没有使用库存,结果证明这是真正的错误。将其标记为已接受。