作为学习如何使用StateT和非确定性monad的一部分,我想编写一个函数,使用它们来枚举整数的分区(同时允许重用整数)。例如,传递4
的参数应该导致[[1,1,1,1],[1,1,2],[2,2],[1,3],[4]]
(唯一性并不重要,我更关心的是只需要使用代码)。
(另外,我知道有一个递归解决方案,用于生成分区以及动态编程和生成基于函数的分区计数解决方案 - 本练习的目的是构建一个最小的工作示例,结合StateT和[]。)
这是我的尝试,旨在处理任何小于或等于5的输入:
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_GHC -Wall #-}
import CorePrelude
import Control.Monad.State.Lazy
sumState :: StateT Int [] [Int]
sumState = do
m <- lift [1..5]
n <- get <* modify (-m+)
case compare n 0 of
LT -> mzero
EQ -> return [m]
GT -> fmap (n:) sumState
runner :: Int -> [([Int],Int)]
runner = runStateT sumState
我使用runStateT
而不是evalStateT
来帮助调试(查看最终状态值很有帮助)。就像我说的那样,我并不太担心生成独特的分区,因为我首先想要了解正确使用这两个monad的方法。
将其加载到GHCi
并评估runner 4
会产生以下结果,并且我对上述代码产生此输出的原因感到困惑。
[([4,3,2,1,1],-1),([4,3,2,1,2],-2),([4,3,2,1,3],-3),([4,3,2,1,4],-4),([4,3,2,1,5],-5),([4,3,2,1],-1),([4,3,2,2],-2),([4,3,2,3],-3),([4,3,2,4],-4),([4,3,2,5],-5),([4,3,1,1],-1),([4,3,1,2],-2),([4,3,1,3],-3),([4,3,1,4],-4),([4,3,1,5],-5),([4,3,1],-1),([4,3,2],-2),([4,3,3],-3),([4,3,4],-4),([4,3,5],-5),([4,2,1,1],-1),([4,2,1,2],-2),([4,2,1,3],-3),([4,2,1,4],-4),([4,2,1,5],-5),([4,2,1],-1),([4,2,2],-2),([4,2,3],-3),([4,2,4],-4),([4,2,5],-5),([4,1,1],-1),([4,1,2],-2),([4,1,3],-3),([4,1,4],-4),([4,1,5],-5),([4,1],-1),([4,2],-2),([4,3],-3),([4,4],-4),([4,5],-5)]
我做错了什么?什么是组合StateT和[]以枚举分区的正确方法?
答案 0 :(得分:2)
你只有两个小错误。第一个是:
n <- get <* modify (-m+)
在我们减去n
之前,这会获得m
的值。你几乎肯定想要
n <- modify (-m+) >> get
代替,或
modify (-m+)
n <- get
如果你喜欢这种拼写。另一个是你将当前状态放在列表中而不是你在GT
分支中添加的值:
GT -> fmap (n:) sumState
将其更改为
GT -> fmap (m:) sumState
你是金色的:
*Main> runner 4
[([1,1,1,1],0),([1,1,2],0),([1,2,1],0),([1,3],0),([2,1,1],0),([2,2],0),([3,1],0),([4],0)]