10.2.3.2 在 C# 中以函数风格使用数组
由于有了 LINQ to Object,在 C# 3.0 中,我们已经可以使用许多函数结构来处理数组。大多数 LINQ 运算符不返回数组:如果在数组上调用 Enumerable.Select,结果将返回 IEnumerable。在某些情况下,我们还是愿意将结果保存在数组中,避免调用Enumerable.ToArray,将结果序列复制回数组的...
分类:
编程语言 时间:
2015-01-03 14:42:13
阅读次数:
205
10.3 连续(continuations)
从本章开始,我们讨论递归调用。我们已经看到过尾递归(tail recursion),这是进行递归调用的重要技术,不需要在栈分配任何空间。由于有了尾递归,写出的函数式列表处理函数,能够处理大型数据集,而不需要出一身汗。
我们已经使用尾递归重写了许多函数,利用累加器参数,但并不是每个函数都能这样重写。如果函数需要执行两个递归调用,那么,很显然不能...
分类:
其他好文 时间:
2015-01-03 14:40:31
阅读次数:
104
10.2.3.1 以函数方式使用数组
我们先来看一个 F# 的例子,这是两个F# 库处理数组的重要的高阶函数,然后,用 C# 实现相同的功能。清单 10.12 的中脚本,先用随机数初始化一个数组,然后,计算出它们的平方。
清单 10.12 处理数组的函数式方法(F# Interactive)
> let rnd = new System.Random();;
val rnd...
分类:
编程语言 时间:
2015-01-02 17:35:06
阅读次数:
226
10.2.3 处理数组
数组相似于简单的计算机内存模型,本质上,就是一组有编号的盒子,只要知道的编号,就可以轻松地读取或更改在任何盒子中的值。数组形成连续的内存块,所以,开销非常小,能用于存储大型数据集;数组[的代间]是提前分配的:一旦创建,其大小就固定了,因此,不能把新的值添加已有的数组中。
数组是可变的数据结构,因此,很容易修改。这在有些情况下是有用的,但对于函数式程序员来说,在有关...
分类:
编程语言 时间:
2015-01-01 17:22:50
阅读次数:
154
10.2.2.1 添加元素到列表
到目前为止,我们已经看到如何追加元素到已有(函数式)列表的前面;如果我们想在列表的末尾追加元素,又该如何做呢?这要求听起来是合理的,那么,我们尝试实现它。清单 10.10 显示了在列表的前面和在后面插入naïve 企图之间性能的差别。
清单10.10 在列表中添加元素(F# Interactive)
> let prepend el list =...
分类:
其他好文 时间:
2015-01-01 00:15:00
阅读次数:
240
10.2.1 用尾递归避免栈溢出(续!)
[
naïve
不像是英语,不知道什么意思。
]
第六章中的列表处理函数并不是尾递归。如果我们传递很大的列表,就会因栈溢出而失败。我们将用尾递归重写两个函数(map 和 filter),将改正这个问题。为了对照,在清单 10.8 中包括了原来的实现。为了避免名字冲突,已经改名为 mapN 和 filterN。
清单10.8 naïv...
分类:
其他好文 时间:
2014-12-30 17:15:25
阅读次数:
182
10.2 处理大集合
我们提到过要回到尾递归,并展示稍许复杂的涉及列表的情况。现在,希望任何由递归引起的头痛都将消失,喝一杯新鲜的咖啡后,为即将到来的例子做好准备。
既要确保我们的程序不引发栈溢出异常,还要在合理的时间内运行。(是什么作出这种不切实际的要求?)函数式列表是令人难以置信的有用,能够非常有效,但如果使用方法不正确,会异常地变慢,我们现在就展示如何避免这些问题。...
分类:
其他好文 时间:
2014-12-22 11:14:17
阅读次数:
98
10.1.2 使用记忆化缓存结果
记忆化(Memoization),可以描述为缓存函数调用的结果,听起来可能有点复杂,但是,技术非常简单。正如我们前面提到的那样,在函数式编程中,大多数函数是没有副作用的,因此,如果我们用相同的参数值,两次调用同一个函数,得到的结果相同。
如果我们要得到与上一次相同的结果,为什么还要麻烦去再一次执行函数呢?相反,我们可以缓存这个结果。如果我们把第一次调用的...
分类:
其他好文 时间:
2014-12-18 17:02:59
阅读次数:
192
10.1.1.1 使用累加器参数
我们考虑一下,如何把 sumList 函数改成尾递归,即,只在参数值是 cons cell (非空的列表)的分支上,执行一次递归调用。我们的经验法则表明,不应该很难,但目前,它做的事情,不止是返回递归调用的结果:把头中的值与总和相加。
要把这个函数变成尾递归函数,可以使用提供累加器参数(accumulator argument)的方法。计算结果时,不再从...
分类:
其他好文 时间:
2014-12-18 15:11:40
阅读次数:
141
10.1.1避免尾递归的堆栈溢出
对于每一个函数调用,运行时分配一个栈帧(stack frame)。这些帧保存在由系统维护的栈中;调用完成,栈帧被删除;如果函数调用其他函数,那么,一个新的帧添加到这个栈的顶部。栈的大小是有限的,所以,太多的嵌套函数调用会耗光了给其他栈帧的空间,就不能再调用下一个函数了。在 .NET 中发生这种情况时,会引发 StackOverflowException 错...
分类:
其他好文 时间:
2014-12-17 18:40:28
阅读次数:
206