【问题标题】:Haskell: Let inside List comprehension unexpected resultHaskell:让内部列表理解意外结果
【发布时间】:2023-03-25 21:06:01
【问题描述】:

我是haskell 新手,我正在努力学习haskell。我正在尝试以我能想到的最可能的方式创建简单的功能,例如“复制”。我已经用模式匹配和守卫做了“复制”。 我无法在列表理解中使用 let 。 我可以想象让列表理解并不理想,但我更感兴趣为什么它不起作用=]。

我的问题是:为什么 duplicate' 产生 [Int] 而 duplicate'' 产生 [[Int]] 甚至可以在列表理解中生成带有 let 的 [Int] 吗?

感谢您的时间和帮助:)。

--list comprehension
duplicate' xs = [y | x <- xs, y <- [x,x]]
input => [1,2,3,4]
output => [1,1,2,2,3,3,4,4]
expected? => yes

--list comprehension with let
duplicate'' xs = [y | x <- xs, let y = [x,x]]
input => [1,2,3,4]
output => [[1,1],[2,2],[3,3],[4,4]]
expected? => yes

【问题讨论】:

  • [y | x &lt;- xs, let y = [x,x]] == [[x,x] | x &lt;- xs] 你看,这不是你想要的

标签: haskell list-comprehension let


【解决方案1】:

&lt;-let 只是意味着不同的东西。

当您编写y &lt;- [x,x] 时,您是在说“将y 列表中的每个值依次分配[x,x]”。

当你写let y = [x,x]时,你是在说“给y赋值[x,x]”。

【讨论】:

    【解决方案2】:

    let 只是定义一个新符号来获取某个给定值。您总是可以手动内联定义:

    [ y | x <- xs, let y = [x,x] ] ≡ [ [x,x] | x <- xs ]
    

    任何只有一个&lt;- 的表达式都有这样的形式

    [ 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 实际上是范畴理论家更喜欢定义单子的方式,但您可能知道 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&lt;-p; b&lt;-q; ... return x } 是一个monad 推导,它是列表推导的泛化。可以改写[x | a&lt;-q, b&lt;-q, ...]。对于我们的问题,

    join [y | x <- xs, let y = [x,x] ] ≡ [y | x<-xs, y<-[x,x]]
    

    这是你开始的地方。使用纯列表理解执行此操作时,不可避免地会使用两个 &lt;-s。

    当然,您仍然可以在任何时候使用 also 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]
         ≡ ...
    

    【讨论】:

    • 我理解 concat 是一个专门的列表连接吗? (或者也许反过来:列表 monad 的加入是用 concat 实现的?)
    • @kqr join(用(&gt;&gt;=)定义,用foldr定义[a])和concat(用foldr定义)是分开定义的,但两者都做列表也是如此。
    • 感谢详细的解释,帮了大忙。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    相关资源
    最近更新 更多