【问题标题】:Haskell: put in State monad seems to be elidedHaskell:放入状态单子似乎被省略了
【发布时间】:2015-04-15 18:16:39
【问题描述】:

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

我的方法是计算((User, Pizza), Int) 来表示一个人想要说比萨的数量,对它们进行排序,然后通过使用状态单子进行递归以保持库存计数。

程序已编写并进行类型检查:

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 的新手,所以任何风格的 cmets 都会受到赞赏 =)

谢谢!

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)

【问题讨论】:

  • 您能否包含buildQuotas 的定义?或者最好你可以将你的代码减少到MCVE
  • 另外,allocatePizzasImpl 不进行类型检查(尝试将(flavor, _) 模式与Int 类型匹配)。

标签: haskell state-monad


【解决方案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

【讨论】:

  • 这是一个比我的更好的实现。谢谢!
【解决方案2】:

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

我很想知道为什么会出现这种情况!

编辑:正如 Daniel Wagner 在 cmets 中指出的那样,我实际上并没有使用库存,事实证明这是真正的错误。将此标记为已接受。

【讨论】:

  • 只是标准的懒惰。如果没有人关心它是什么,为什么还要计算结果状态? (请注意,您实际上并没有使用inventory 来做出任何决定,除了下一个库存应该是什么!)
  • 好极了。不使用库存是真正的错误。干杯!
猜你喜欢
  • 2021-10-28
  • 2016-09-30
  • 2012-03-11
  • 1970-01-01
  • 1970-01-01
  • 2017-12-05
  • 2012-03-08
  • 2015-12-11
  • 2014-04-15
相关资源
最近更新 更多