【发布时间】:2020-07-21 16:09:52
【问题描述】:
这个问题是从我的previous question 扩展而来的,关于可变值。我很确定这个问题的主题 pre-computation 与链接的问题有很多关系。
请看下面的例子,这些例子来自我正在学习的书中:
let isWord (words : string list) =
let wordTable = Set.ofList words // Expensive computation!
fun w -> wordTable.Contains(w)
val isWord : words:string list -> (string -> bool)
它接受一个字符串列表,并返回检查输入字符串是否在列表中的函数。有了这个小巧可爱的辅助函数,这里有两个例子:
let isCapital = isWord ["London"; "Paris"; "Warsaw"; "Tokyo"];;
val isCapital : (string -> bool)
let isCapitalSlow word = isWord ["London"; "Paris"; "Warsaw"; "Tokyo"] word
val isCapitalSlow : (string -> bool)
我认为这两个函数做的事情完全一样,但事实并非如此。书中说,虽然第一个 预先计算给定列表中的集合,但第二个将在函数调用时计算集合。
正如我在 PL 课中学到的,为了计算 lambda 演算表达式,每个参数都应该被赋予函数体。仅缺少一个将不允许对表达式进行评估。
基于此,我得出结论,第一个没有参数,因此它可以在给出列表时立即开始评估,但第二个在给出参数 word 之前无法开始评估。到这里为止都很好,但是在考虑了上面链接的问题之后,我不确定我是否正确理解它。
从它和相关问题的答案中思考,评估似乎一直持续到它变得无法评估,可能是因为缺乏信息、参数或任何东西。那么,是否可以认为表达式的每个 situation-free 部分将只计算一次并预先计算,就像第一个示例一样?
这部分似乎对优化和性能有很大影响,所以我想澄清一下我对这个主题的理解。
【问题讨论】:
-
没有检查,似乎 isCapital 是一个值。该值是一个函数,在调用 isWord 时返回。因此,计算 isCapital 时会计算一次 wordTable。但是 isCapitalSlow 不是一个值——它是一个函数,所以每次调用它时都会调用 isWord。
-
如果还不清楚,请记住函数是 F# 语言的一等成员。 isCapital 是一个值,但该值是一个计算出来的函数。另一方面,isCapitalSlow 是编码(术语?)函数,而不是计算函数,因此不是值。 isWord 是一个计算函数的函数。