首先,我会将理解翻译为单调表达式。
x >>= \i -> x >>= \j -> x >>= \k -> x >>= \l -> return [i,j,k,l]
随着n = 4
我们看到我们有4 x
的,一般都会有n
x
的。因此,我在考虑长度为n
的x
的列表。
[x,x,x,x] :: [[a]]
我们该如何从[x,x,x,x]
转到monadic表达式?第一个好的猜测是foldr
,因为我们想要对列表中的每个元素进行一些操作。特别是,我们希望从每个x
中获取一个元素并用这些元素形成一个列表。
foldr :: (a -> b -> b) -> b -> [a] -> b
-- Or more accurately for our scenario:
foldr :: ([a] -> [[a]] -> [[a]]) -> [[a]] -> [[a]] -> [[a]]
有两个方面拿出了foldr相似,我会打电话f :: [a] -> [[a]] -> [[a]]
和z :: [[a]]
。我们知道什么是foldr f z [x,x,x,x]
:
foldr f z [x,x,x,x] = f x (f x (f x (f x z)))
如果加上括号前面的一元表达,我们有这样的:
x >>= \i -> (x >>= \j -> (x >>= \k -> (x >>= \l -> return [i,j,k,l])))
你可以看到两个表达式是如何寻找相似。我们应该能够找到一个f
和z
使它们相同。如果我们选择f = \x a -> x >>= \x' -> a >>= \a' -> return (x' : a')
我们得到:
f x (f x (f x (f x z)))
= (\x a -> a >>= \a' -> x >>= \x' -> return (x' : a')) x (f x (f x (f x z)))
= f x (f x (f x z)) >>= \a' -> x >>= \x' -> return (x' : a')
= f x (f x (f x z)) >>= \a' -> x >>= \l -> return (l : a')
= (f x (f x z) >>= \a' -> x >>= \k -> return (k : a')) >>= \a' -> x >>= \l -> return (l : a')
= f x (f x z) >>= \a' -> x >>= \k -> x >>= \l -> return (l : k : a')
- 请注意,我已经逆转
i,j,k,l
顺序l,k,j,i
但在寻找组合的情况下,这应该是不相关的。如果真的担心,我们可以试试a' ++ [x']
。
的最后一步是因为(a >>= \b -> c) >>= \d -> e
相同a >>= \b -> c >>= \d -> e
(占可变卫生时)和return a >>= \b -> c
相同(\b -> c) a
。
如果我们继续展开这个表达式,最终我们将在前面达到z >>= \a' -> …
。这里唯一有意义的选择是z = [[]]
。这意味着foldr f z [] = [[]]
可能并不理想(改为[]
)。相反,我们可能使用foldr1
(对于非空列表,我们可能使用Data.NonEmpty
),或者我们可能会为空列表添加一个单独的子句到combs
。
看着f = \x a -> x >>= \x' -> a >>= \a' -> return (x' : a')
我们可能会意识到这种有用的等价性:a >>= \b -> return (c b) = c <$> a
。因此,f = \x a -> x >>= \x' -> (x' :) <$> a
。然后还有a >>= \b -> c (g b) = g <$> a >>= \b -> c
等f = (:) <$> x >>= \x' -> x' <$> a
。最后,a <*> b = a >>= \x -> x <$> b
等等f = (:) <$> x <*> a
。
The official implementationsequenceA
列表是foldr (\x a -> (:) <$> x <*> a) (pure [])
,正是我们在这里想到的。这可以进一步缩短为foldr (liftA2 (:)) (pure [])
,但可能存在一些优化差异,使得实现者不选择此选项。
最后一步是仅仅想出一个n
x
的列表。这只是replicatereplicate n x
。碰巧有一个既有复制又有排序的功能,叫做replicateMreplicateM n x
。
您可以先提供目标。你想在这里做什么? –
@WillemVanOnsem给定数字n和一组字符x,从长度为n的x生成符号的每个组合。例如x =“abc”和n = 2会给出[“aa”,“ab”,“ac”,“ba”,“bb”,“bc”,“ca”,“cb”,“cc”] 。 – Stewart
列表解析对于某些单点操作来说实际上是一个很好的捷径。 (事实上,有一个GCH扩展允许一个生成器是一个任意的monad,而不仅仅是一个列表。)但是,你不应该将它们视为执行它们的方式。 – chepner