需要帮助理解延续功能

时间:2015-10-20 01:27:34

标签: functional-programming sml smlnj

这是一个教学示例来说明CPS和尾递归:

fun sum [] k = k 0
  | sum (x::xs) k = sum xs (fn y=>k(x+y));

我无法理解匿名函数fn y=>k(x+y)如何正确地总结输入列表的元素。

根据我的理解,匿名函数意味着一个带有一个参数y的新函数,其中函数体用参数k调用原始函数y+x

如果我调用sum [1,2,3,4,5] (fn x=>x);,我会得到15.如果我有sum [1,2,3,4,5] (fn x=>3x);,答案是45.因此,sum函数的用户必须首先了解sum的确切细节。 {1}}只有k的适当版本才能生成给定列表的总和。以这种方式提供用户提供功能的真实目的是什么?

如果我是sum函数的作者,我无法控制用户将传递给k的内容。换句话说,我如何指定函数将精确地做什么?

2 个答案:

答案 0 :(得分:3)

  

sum函数的用户因此必须首先理解sum的确切血腥细节,因为只有k的适当版本才会产生给定列表的总和。

没有。一如既往,阅读文档应该足够了,不需要查看实现细节。您的k 给出了列表的确切总和 - 这就是重要的一切。您应该将k理解为output parameter(虽然没有变异);它基本上是callback

  

如果我是sum函数的编写者,我无法控制用户将传递给k的内容。换句话说,我如何指定函数将精确地做什么?

您无需关心用户传递的内容。您只需记录函数的作用:它使用提供的k列表的总和调用提供的xs。返回值并不重要。

  

以这种方式提供用户提供功能的真实目的是什么?

如果采取极端措施,您无需在continuation-passing style中返回任何值 - 您只需将其传递给回调即可。这使得调用堆栈变得多余。从另一个角度来看,每个函数都以尾调用结束,可以优化而不是返回。

答案 1 :(得分:1)

  

我无法理解 [...] 如何正确地总结输入列表的元素。

手动尝试并评估您的功能:

sum [1,2,3] id
sum [2,3] (fn y1=>id(1+y1))
sum [3] (fn y2=>(fn y1=>id(1+y1))(2+y2))
sum [] (fn y3=>(fn y2=>(fn y1=>id(1+y1))(2+y2))(3+y3))
(fn y3=>(fn y2=>(fn y1=>id(1+y1))(2+y2))(3+y3)) 0
(fn y2=>(fn y1=>id(1+y1))(2+y2))(3+0)
(fn y2=>(fn y1=>id(1+y1))(2+y2)) 3
(fn y1=>id(1+y1))(2+3)
(fn y1=>id(1+y1)) 5
id(1+5)
id(6)
6

正如您所看到的,此函数在堆内存中构建了一系列匿名函数,最终相互调用。正常的递归函数将使用堆栈空间作为等效函数。

  

sum函数的用户因此必须首先理解sum的确切血腥细节,因为只有适当的k版本才能产生给定列表的总和。以这种方式提供用户提供功能的真实目的是什么?

正如Bergi所写,用户不需要理解sum函数的工作原理,只需要继续作为参数并在其基本情况下解析它。正如Bergi所写,它不必在其基本情况下评估k。该功能的替代方案是:

fun sum [] k = k
  | sum (x::xs) k = sum xs (fn y=>k(x+y));

这里的一个应用程序,以及使用回调作为参数和返回值导出sum函数的理由是,您可以通过这种方式懒洋洋地链接函数。例如,使用上述功能,您可以汇总列表列表;

fun sumMany [] k = k
  | sumMany (xs::xss) k = sumMany xss (sum xs k)

您可能会像

一样评估它
val result = sumMany [[1,2,3],[4,5,6],[7,8,9]] (fn x=>x) 0