【问题标题】:Haskell Let Expressions EvaluationHaskell 让表达式求值
【发布时间】:2019-09-06 23:29:56
【问题描述】:

我正在做计算 let 表达式的练习题,但我不理解这个表达式的输出。

这是表达式:

let a = 2
    b = 1:[i * 2 | i <- b]
    f a = 1:[i * a | i <- (f a)]
in take (a+2) (f (head (tail b) ))

输出应该是 [1,2,4,8]。有人可以逐步解释为什么这是输出

【问题讨论】:

    标签: haskell stream expression evaluation let


    【解决方案1】:

    这里是一步一步的解释:

    let a = 2
        b = 1:[i * 2 | i <- b]
        f a = 1:[i * a | i <- (f a)]
    in take (a+2) (f (head (tail b) ))
    

    其中有两个不同的变量a,一个隐藏另一个,所以首先让我们重命名其中一个以避免意外混淆:

    let outer_a = 2
        b = 1:[i * 2 | i <- b]
        f a = 1:[i * a | i <- (f a)]
    in take (outer_a+2) (f (head (tail b) ))
    

    现在我们可以替换 outer_a 并评估 +

    let b = 1:[i * 2 | i <- b]
        f a = 1:[i * a | i <- (f a)]
    in take 4 (f (head (tail b) ))
    

    根据map重写列表推导:

    let b = 1:map (* 2) b
        f a = 1:map (* a) (f a)
    in take 4 (f (head (tail b) ))
    

    使用iterate 而不是显式递归:

    let b = iterate (* 2) 1
        f a = iterate (* a) 1
    in take 4 (f (head (tail b) ))
    

    评估b的前两步:

    let b = 1:2:iterate (* 2) 4
        f a = iterate (* a) 1
    in take 4 (f (head (tail b) ))
    

    替换成b:

    let f a = iterate (* a) 1
    in take 4 (f (head (tail (1:2:iterate (* 2) 4)) ))
    

    评估tail

    let f a = iterate (* a) 1
    in take 4 (f (head (2:iterate (* 2) 4) ))
    

    评估head

    let f a = iterate (* a) 1
    in take 4 (f 2)
    

    替换成f a:

    take 4 (iterate (* 2) 1)
    

    评估iterate几次:

    take 4 (1:2:4:8:iterate (* 2) 16)
    

    评估take

    [1,2,4,8]
    

    我们完成了。

    【讨论】:

    • 这实际上是一个即将到来的考试的练习题,据说解决方案是[1,2,4,8]
    • @csj 那么要么练习题错了,要么你抄错了。如果您在 GHCi 中准确输入您在问题中发布的内容(当然是在完成:set +m 之后),它会给您[1,3,9,27]
    • 哦,很抱歉它应该是 * 而不是 + 代表 b,我刚刚编辑了问题
    • f a 当然会返回与iterate (*a) 1 相同的值,但是它以一种非常不同的方式完成它,速度非常慢(是二次的而不是线性的)。如果我们逐步减少它可以看出这一点。
    【解决方案2】:

    要了解发生了什么,我们会仔细命名每个实体:

    let a   = 2
        b   = 1 : [i * 2 | i <- b]
        f a = 1 : [i * a | i <- f a]
    in  take (a+2) (f (head (tail b)))
    ==
    let b        = (b1:bs1)
        (b1:bs1) = 1 : [i * 2 | i <- b]
    in  take 4 (f (head (tail b)))
    ==
    let b1  = 1
        bs1 = [i * 2 | i <- (b1:bs1)]
    in  take 4 (f (head bs1))
    ==
    let b1  = 1
        bs1 = [i * 2 | i <- [b1]] ++ [i * 2 | i <- bs1]
    in  take 4 (f (head bs1))
    ==
    let bs1 = [i * 2 | i <- [1]] ++ [i * 2 | i <- bs1]
    in  take 4 (f (head bs1))
    ==
    let bs1      = (b2:bs2)
        (b2:bs2) = [1 * 2] ++ [i * 2 | i <- bs1]
    in  take 4 (f b2)
    ==
    let (b2:bs2) = 2 : [i * 2 | i <- (b2:bs2)]
    in  take 4 (f b2)
    ==
    let bs2      =     [i * 2 | i <- (2:bs2)]
        f a      = 1 : [i * a | i <- f a]     -- same as before
    in  take 4 (f 2)
    ==
    let xs       = f 2
        f 2      = 1 : [i * 2 | i <- f 2] 
    in  take 4 xs
    ==
    let (x1:xs1) = 1 : [i * 2 | i <- f 2]
    in  take 4 (x1:xs1)
    ==
    let xs1      =     [i * 2 | i <- f 2]
    in  take 4 (1:xs1)
    ==
    let xs1      =     [i * 2 | i <- f 2]
    in  1 : take 3 xs1
    ==
    let (x2:xs2) =     [i * 2 | i <- (y1:ys1)]
        (y1:ys1) = 1 : [i * 2 | i <- f 2]
    in  1 : take 3 (x2:xs2)
    ==
    let (x2:xs2) =     [i * 2 | i <- (1:ys1)]
        ys1      =     [i * 2 | i <- f 2]
    in  1 : take 3 (x2:xs2)
    ==
    let (x2:xs2) = 2 : [i * 2 | i <- ys1]
        ys1      =     [i * 2 | i <- f 2]
    in  1 : take 3 (x2:xs2)
    ==
    let xs2      =     [i * 2 | i <- ys1]
        ys1      =     [i * 2 | i <- f 2]
    in  1 : take 3 (2:xs2)
    ==
    let xs2      =     [i * 2 | i <- ys1]
        ys1      =     [i * 2 | i <- f 2]
    in  1 : 2 : take 2 xs2
    ==
    let (x3:xs3) =     [i * 2 | i <- (y2:ys2)]
        (y2:ys2) =     [i * 2 | i <- (z1:zs1)]
        (z1:zs1) = 1 : [i * 2 | i <- f 2]
    in  1 : 2 : take 2 (x3:xs3)
    ==
    let (x3:xs3) =     [i * 2 | i <- (y2:ys2)]
        (y2:ys2) = 2 : [i * 2 | i <- zs1]
        zs1      =     [i * 2 | i <- f 2]
    in  1 : 2 : take 2 (x3:xs3)
    ==
    let (x3:xs3) = 4 : [i * 2 | i <- ys2]
        ys2      =     [i * 2 | i <- zs1]
        zs1      =     [i * 2 | i <- f 2]
    in  1 : 2 : take 2 (x3:xs3)
    ==
    let xs3      =     [i * 2 | i <- ys2]
        ys2      =     [i * 2 | i <- zs1]
        zs1      =     [i * 2 | i <- f 2]
    in  1 : 2 : 4 : take 1 xs3
    ==
    let (x4:xs4) =     [i * 2 | i <- (y3:ys3)]
        (y3:ys3) =     [i * 2 | i <- (z2:zs2)]
        (z2:zs2) =     [i * 2 | i <- (w1:ws1)]
        (w1:ws1) = 1 : [i * 2 | i <- f 2]
    in  1 : 2 : 4 : take 1 (x4:xs4)
    ==
    let (x4:xs4) =     [i * 2 | i <- (y3:ys3)]
        (y3:ys3) =     [i * 2 | i <- (z2:zs2)]
        (z2:zs2) = 2 : [i * 2 | i <- ws1]
        ws1      =     [i * 2 | i <- f 2]
    in  1 : 2 : 4 : take 1 (x4:xs4)
    ==
    let (x4:xs4) =     [i * 2 | i <- (y3:ys3)]
        (y3:ys3) = 4 : [i * 2 | i <- zs2]
        zs2      =     [i * 2 | i <- ws1]
        ws1      =     [i * 2 | i <- f 2]
    in  1 : 2 : 4 : take 1 (x4:xs4)
    ==
    let (x4:xs4) = 8 : [i * 2 | i <- ys3]
        ys3      =     [i * 2 | i <- zs2]
        zs2      =     [i * 2 | i <- ws1]
        ws1      =     [i * 2 | i <- f 2]
    in  1 : 2 : 4 : take 1 (x4:xs4)
    ==
        1 : 2 : 4 : 8 : take 0 xs4
    ==
        1 : 2 : 4 : 8 : []
    

    在上面的推导中,我们使用了列表推导的属性

      [ ... | ... <- (xs   ++   ys)] 
    === 
      [ ... | ... <- xs ] ++ [ ... | ... <- ys]
    

    这样

      [ ... | ... <- (x : ys)] 
    === 
      [ ... | ... <- [x] ] ++ [ ... | ... <- ys]
    

    f a 产生与iterate (* a) 1 相同的结果,但在操作上却大不相同。虽然后者是线性的,但前者是二次函数,w.r.t.它的时间复杂度。

    要了解它在实践中的含义,请比较以下时间:

    > f 1.01 !! 4000
    1.9297236994732192e17
    (1.28 secs, 1614556912 bytes)
    
    > iterate (* 1.01) 1 !! 4000
    1.9297236994732192e17
    (0.00 secs, 12990984 bytes)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-14
      • 2015-09-02
      • 1970-01-01
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多