let
除了定义一个新符号来获取某个给定值之外,什么也不做。您总是可以手动内联定义:
[ y | x <- xs, let y = [x,x] ] ≡ [ [x,x] | x <- xs ]
任何只有一个<-
的表达式因此具有形式
[ f x | x<-xs ]
这相当于map f xs
。因此,结果列表必须始终具有与 相同的长度xs
,从而无法实现所需的行为duplicate
:如果您想要重复,则需要将它们封装为内部列表,这样它们就不会算作更多元素。
要将这些嵌套列表合并回“平面”列表,您可以利用列表是单子这一事实:
join :: Monad m => m (m a) -> m a
Prelude Control.Monad> 加入 [[1,1], [2,2]]
[1,1,2,2]
现在,join
实际上是范畴论者更喜欢定义 monad 的方式,但你可能知道 Haskell 的做法有点不同:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
a >>= f = join (fmap f a)
或者,正如它实际上定义的相反,
join a = a >>= id
将其放入修改后的let
版本中duplicate
:
join [y | x <- xs, let y = [x,x] ]
≡ [y | x <- xs, let y = [x,x] ] >>= id
≡ map (\x -> [x,x]) xs >>= id
≡ xs >>= id . (\x -> [x,x])
≡ xs >>= (\x -> [x,x])
≡ do { x<-xs; [x,x] }
≡ do { x<-xs; y<-[x,x]; return y } -- by the monad laws
现在,一个表达式do { a<-p; b<-q; ... return x }
是一个单子推导,列表推导的泛化。它可以重写[x | a<-q, b<-q, ...]
。对于我们的问题,
join [y | x <- xs, let y = [x,x] ] ≡ [y | x<-xs, y<-[x,x]]
这是你开始的地方。<-
使用纯列表推导式执行此操作时,不可避免地会使用两个s。
当然,你仍然可以在任何时候使用 let ......
[y | x<-xs, y<-[x,x]]
≡ [y | x<-xs, let z=[x,x], y<-z]
≡ [a | x<-xs, let z=[x,x], y<-z, let a=y]
≡ [a | x<-xs, let z=let w=[let q=x in q, let r=x in r] in w, y<-z, let a=y]
≡ ...