【问题标题】:foldTree step-by-step evaluationfoldTree 逐步评估
【发布时间】:2021-11-06 04:10:13
【问题描述】:

假设foldTreeTree 和函数f 的这些定义:

foldTree : (a -> [b] -> b) -> Tree a -> b
foldTree f = go
    where
        go (Node x ts) = f x (map go ts)

tree :: Tree String
tree = Node
      "Alpha"
      [ Node "Beta" [Node "Epsilon" [], Node "Zeta" [], Node "Eta" []]
      , Node "Gamma" [Node "Theta" []]
      , Node "Delta" [Node "Iota" [], Node "Kappa" [], Node "Lambda" []]
      ]

f x xs = [x] <> (map ('\t' :) (concat xs))

我会尝试评估foldTree f tree

  foldTree f tree
= go (Node "Alpha" [ Node "Beta" [Node "Epsilon" [], Node "Zeta" [], Node "Eta" []], Node "Gamma" [Node "Theta" []], Node "Delta" [Node "Iota" [], Node "Kappa" [], Node "Lambda" []]]
= f "Alpha" (map go [...])
...

此时,我的问题是:等式推理如何与闭包一起工作? go的定义有f;但是,这肯定不在其参数之间。是否有一些技巧可以插入该定义?

【问题讨论】:

    标签: haskell functional-programming scope closures evaluation


    【解决方案1】:

    简单地说,定义可以引用范围内的任何名称。

    “闭包” 是一个与突变存在更相关的概念。在没有的 Haskell 中,“范围” 的概念就足够了。

    go 的定义嵌套在foldTree 的定义中,因此可以访问其参数f。也就是说,在go的定义中,f在作用域内。

    定义可以改写为

    {-
    foldTree f t = go t
        where
             go (Node x ts) = f x (map go ts)
    -}
    foldTree f t =
       let { go (Node x ts) = f x (map go ts) } 
       in go t
    

    并且任何调用 foldTree f1 t1 都被评估为

    > foldTree f1 t1 
    =>
      let { f=f1; t=t1 }
      in
        let { go (Node x ts) = f x (map go ts) } 
        in go t
    =>
      ....
    

    这些是简单的嵌套lets,因此内部的可以访问外部定义的每个名称。

    为了更容易了解发生了什么,请先尝试使用更简单的示例数据进行评估,例如 Node "Alpha" []Node "Alpha" [Node "Beta" []] 等。


    例如,用

    f0 x xs = [x] <> (map ('\t' :) (concat xs))
    

    Node "Alpha" [] 的评估结果为

    > foldTree f0 (Node "Alpha" []) 
    =>
      let { f=f0; t=Node "Alpha" [] }
      in
        let { go (Node x ts) = f x (map go ts) } 
        in go t
    =>
      let { f x xs = [x] <> (map ('\t' :) (concat xs))
          ; go (Node x ts) = f x (map go ts) 
          } 
      in go (Node "Alpha" [])
    =>
      let { f x xs = [x] <> (map ('\t' :) (concat xs))
          ; go (Node x ts) = f x (map go ts) 
          ; (Node x1 ts1) = (Node "Alpha" [])
          } 
      in f x1 (map go ts1)
    =>
      let { f x xs = [x] <> (map ('\t' :) (concat xs))
          ; go (Node x ts) = f x (map go ts) 
          ; x1 = "Alpha"
          ; ts1 = []
          ; xs1 = map go ts1
          } 
      in [x1] <> (map ('\t' :) (concat xs1))
    =>
      ["Alpha"] <> (map ('\t' :) (concat []))
    =>
      ["Alpha"] <> (map ('\t' :) [])
    =>
      ["Alpha"] <> []
    =>
      ["Alpha"]
    

    【讨论】:

    • 非常感谢@Will Ness !!一个问题:在第一个代码块中,您有一个where 子句和一个let 表达式,我无法编译它。有办法解决吗?
    • 太棒了。现在它完美地工作了。关于第二个代码块,您能告诉我如何使用let 替换定义吗?那里有很多in 关键字。理解它有什么诀窍吗?
    • 每个let 都只有一个in,它紧随其后。想象一下括号中的内部let...in...let { f=f1; t=t1 } in ( let { go (Node x ts) = f x (map go ts) } in ( go t ) )。虽然这些括号当然不是必需的,但无论如何都会这样解析代码。
    • 我看到了内部let 的来源。但是,我看不到最外面的let 来自哪里。您对 foldTree 的定义不包括它。你能解释一下吗?
    • 我想我开始明白了!再次感谢你的帮助。你能告诉我这个评价是否正确吗?
    猜你喜欢
    • 2014-10-20
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-19
    • 2022-01-07
    相关资源
    最近更新 更多