【发布时间】:2023-03-10 14:16:01
【问题描述】:
如果输入可以采用无限多的值,则使用列表对不确定性建模是有问题的。例如
pairs = [ (a,b) | a <- [0..], b <- [0..] ]
这将返回 [(0,1),(0,2),(0,3),...] 并且永远不会向您展示任何第一个元素不是 0 的对。
使用Cantor pairing function 将列表列表折叠成单个列表可以解决此问题。例如,我们可以定义一个类似绑定的运算符,通过
对其输出进行更智能的排序(>>>=) :: [a] -> (a -> [b]) -> [b]
as >>>= f = cantor (map f as)
cantor :: [[a]] -> [a]
cantor xs = go 1 xs
where
go _ [] = []
go n xs = hs ++ go (n+1) ts
where
ys = filter (not.null) xs
hs = take n $ map head ys
ts = mapN n tail ys
mapN :: Int -> (a -> a) -> [a] -> [a]
mapN _ _ [] = []
mapN n f xs@(h:t)
| n <= 0 = xs
| otherwise = f h : mapN (n-1) f t
如果我们现在把它包装成一个 monad,我们可以枚举所有可能的对
newtype Select a = Select { runSelect :: [a] }
instance Monad Select where
return a = Select [a]
Select as >>= f = Select $ as >>>= (runSelect . f)
pairs = runSelect $ do
a <- Select [0..]
b <- Select [0..]
return (a,b)
这会导致
>> take 15 pairs
[(0,0),(0,1),(1,0),(0,2),(1,1),(2,0),(0,3),(1,2),(2,1),(3,0),(0,4),(1,3),(2,2),(3,1),(4,0)]
这是一个更理想的结果。但是,如果我们要改为三元组,输出的顺序就不是“很好”,而且我什至不清楚所有输出最终都包括在内--
>> take 15 triples
[(0,0,0),(0,0,1),(1,0,0),(0,1,0),(1,0,1),(2,0,0),(0,0,2),(1,1,0),(2,0,1),(3,0,0),(0,1,1),(1,0,2),(2,1,0),(3,0,1),(4,0,0)]
请注意,(2,0,1) 在排序中出现在(0,1,1) 之前——我的直觉是,这个问题的一个好的解决方案将根据“大小”的一些概念对输出进行排序,这可能是算法的显式输入,或者可以隐式给出(如本例中,输入的“大小”是其在输入列表中的位置)。组合输入时,组合的“大小”应该是输入大小的某个函数(可能是总和)。
我想念的这个问题有什么优雅的解决方案吗?
【问题讨论】:
-
你能把 [] 换成 logict 吗?
-
也许吧!我将看看它是如何实现的。主要出于教育原因对此感兴趣,而不是因为我想将其用于某事。
-
这真的很酷;我不知道如何给它一个漂亮的单子接口,但也许空间填充曲线的概念可以给你你想要的行为(因为它们可以是 n 维的)?
标签: haskell monads non-deterministic