这是一个教学示例来说明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
的内容。换句话说,我如何指定函数将精确地做什么?
答案 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