2.2.2 使用不可变数据结构

函数程序表示数据,使用数据结构,我们会在第五、七章讨论数据结构。虽然数据结构的概念通常更简单,但是,我们现在要讨论复合数据类型,比如 C# 值类型,或类。从第一章我们知道,函数编程中的数据结构是不可变的。

不可变数据结构的概念,逻辑上可以从不可变值绑定的概念推导出来。典型的数据结构包含字段声明。如果我们不可变性的概念从变量声明扩展到字段声明,就能得出一切都是不可变的。在 C# 中,不可变类字段要使用 readonly 限定符,而 F# 中所有的数据结构都是不可变的,是默认情况。F# 并不是严格的函数语言,因此,它也可以创建可变类型。

此刻,我们知道如何使用不可变的数据结构,以及如何在 C# 中创建不可变类。使用这种数据结构的类的方法或函数,不能修改结构的状态,唯一可以做的就是返回值,使用这种数据结构的所有操作,结果只能返回新值。C# 中的字符串类型的行为就是如此,str.Substring(0, 5) 的结果会得到一个新的字符串,原来的字符串保持不变。

我们在第一章曾提到过,函数代码常常写成表达式,而不是语句序列。对代码的这种不同理解,使程序更具声明性,因此,使用不可变数据结构也支持函数风格。假设我们有一个类表示函数式集合,它有一个创建空列表的操作,另一个将数字“添加”到列表的操作。因为列表是不可变的,添加元素不能改变原来的列表;相反,操作会返回新的列表,其中包含原始列表的项目和新添加的元素。如果我们想要创建一个列表,并添加元素,代码可以这样写:

var res =ImmutableList.Empty<int>().Add(1).Add(3).Add(5).Add(7);

如果用可变列表,实现同样的功能,我们必须创建列表,然后通过调用命令式的 Add 方法对列表进行修改。结果,要写一个变量声明和四个语句(源代码总数可能会是五行)。这个示例表明,不可变数据结构通常有助于写出更简洁的代码。当然,命令式语言也有办法达到类似的效果,但是,用函数风格不需要额外的付出。

到目前为止,我们已经知道函数语言使用不可变的数据结构和不可变的值,而不是可变的变量。可以想象一下,不使用传统的变量和赋值运算,如何写出一些非常简单的程序。但是,一旦开始思考更复杂的问题,事情会变得困难,直到你的世界观改变为止。在下一节,我们会看到如何用函数风格实现更为复杂的计算。

2.2.2 使用不可变数据结构

时间: 2024-10-19 21:55:56

2.2.2 使用不可变数据结构的相关文章

11.1.2.1 使用可变数据结构

在清单11.4 中,可以看到两个函数,处理的集合保存了前面示例的地名.这一次,我们使用C#,把地名保存在标准的List<T> 类型中,它是可变的. 清单11.4 处理保存在List<T> 中的地名(C#) List<string> LoadPlaces() {     [1] returnnew List<string> { "Seattle", "Prague", "NewYork", "

11.1.2.2 使用不可变数据结构

为了演示了以不可变风格,写相同的代码,但不一定必须使用函数式列表:即使使用标准的 List<T> 类型,我们一样能够避免修改集合.然而,不幸的是,要确保不意外修改列表,可能很难. 处理不允许修改的类型,有更好的方法.可以使用真正不可变类型,比如,第三章的FuncList<T>,或者 .NET Framework 中的 ReadOnlyCollection<T>:即使使用 IEnumerable<T>,一样可以得到良好的安全保障:可用于枚举任何集合类型的元素(

python 数据结构 初学时没太注意却发现很有用的点点滴滴

1. list.extend(L) 将指定列表中的所有元素附加到另一个列表的末尾:相当于a[len(a):] = L. 2. list.pop([i]) 删除列表中指定位置的元素并返回它.如果未指定索引,a.pop()将删除并返回列表中的最后一个元素.(i 两边的方括号表示这个参数是可选的,而不是要你输入方括号.你会在 Python 参考库中经常看到这种表示法). 3. list.index(x) 返回列表中第一个值为 x 的元素的索引.如果没有这样的元素将会报错. 4. list.count(

Python3 官方文档翻译 - 5 数据结构

这章会更详细地描述了一些你已经学过的知识,同时添加一些新东西. 5.1 List进阶 下面是关于List的所有方法 list.append(x) 将元素添加至列表尾,相当于a[len(a):] = [x] list.extend(L) 通过将L中所有元素添加至列表尾来扩展list,相当于a[len(a):] = L list.insert(i, x) 在指定位置插入元素.第一个参数是插入位置前一个的下标,a.insert(0,x)是在列表头插入,a.insert(len(a),x)相当于a.ap

探索c#之不可变数据类型

阅读目录: 不可变对象 自定义不可变集合 Net提供的不可变集合 不可变优点 不可变对象缺点 不可变对象 不可变(immutable): 即对象一旦被创建初始化后,它们的值就不能被改变,之后的每次改变都会产生一个新对象. var str="mushroomsir"; str.Substring(0, 6) c#中的string是不可变的,Substring(0, 6)返回的是一个新字符串值,而原字符串在共享域中是不变的.另外一个StringBuilder是可变的,这也是推荐使用Stri

JVM学习(2)——技术文章里常说的堆,栈,堆栈到底是什么,从os的角度总结--转载http://www.cnblogs.com/kubixuesheng/p/5202561.html

转载自---http://www.cnblogs.com/kubixuesheng/p/5202561.html 俗话说,自己写的代码,6个月后也是别人的代码--复习!复习!复习!涉及到的知识点总结如下: 堆栈是栈 JVM栈和本地方法栈划分 Java中的堆,栈和c/c++中的堆,栈 数据结构层面的堆,栈 os层面的堆,栈 JVM的堆,栈和os如何对应 为啥方法的调用需要栈 属于月经问题了,正好碰上有人问我这类比较基础的知识,无奈我自觉回答不是有效果,现在深入浅出的总结下: 前一篇文章总结了:JV

Group Anagrams

Given an array of strings, group anagrams together. For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"], Return: [ ["ate", "eat","tea"], ["nat",

2,理解JVM

一.内存管理: 1,内存结构: 栈和堆区别,栈是连续内存区,一般是2M单位,堆是不连续的链表.受限于虚拟内存,new时分配 PC寄存器.java栈.堆.方法区.本地方法区.运行常量池 java栈:对应一个线程,每个栈中的栈帧关联每个方法,运行一个方法创建一个栈帧,执行完就弹出栈帧.不是线程共享,不用关心数据一致性和同步锁这些线程问题. 堆:程序猿最关心的,是new对象和对象数组时创建的,所有线程共享,保证一致性. 方法区:储存类结构信息,比如class文件解析后,常量池,方法数据,方法体,构造函

Java中堆内存和栈内存详解

Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用. 堆内存用于存放由new创建的对象和数组.在堆中分配的内存,由java虚拟机自动垃圾回收器来管理.在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中