11.1.2.1 使用可变数据结构

在清单11.4 中,可以看到两个函数,处理的集合保存了前面示例的地名。这一次,我们使用C#,把地名保存在标准的List<T> 类型中,它是可变的。

清单11.4 处理保存在List<T> 中的地名(C#)

List<string> LoadPlaces() {     [1]

returnnew List<string> { "Seattle", "Prague",

"NewYork", "Grantchester", "Cambridge" };

}

void PrintLongest(List<string> names){   [2]

varlongest = names[0];   <-- 第一外地名

for(inti = 1; i < names.Count; i++)

if(names[i].Length > longest.Length) longest = names[i];   <-- 记住当前最长的名字

Console.WriteLine(longest);

}

void PrintMultiWord(List<string> names){   [3]

names.RemoveAll(s=> !s.Contains(" "));   <-- 删除只有一个字的名字

Console.WriteLine("Withspace: {0}", names.Count);

}

第一个函数加载样本数据[1],像前面的loadPlaces 函数一样,除了没有人口值;接下来,实现了两个处理函数,第一个函数[2]查找名字最长的地方,第二个函数[3]通过删除不包含空格的名字,确定包含多个单词地名的数量。尽管方法使用了lambda 函数的语法,但是,肯定不是函数式的:因为RemoveAll 方法修改了names 集合。如果我们要在后面的程序中使用这些函数,可以这样写代码:

PrintMultiWord(LoadPlaces());   // Prints‘1‘

PrintLongest(LoadPlaces());     // Prints ‘Grantchester‘

虽然得到了正确的结果,但是,两次调用LoadPlaces 函数,这似乎是不必要的。如果函数从数据库中加载数据,出于性能的原因,只一次检索数据会更好。可以进行简单的重构,只一次调用函数,把地名保存在局部变量中:

var places = LoadPlaces();

PrintMultiWord(places);  // Prints ‘1‘

PrintLongest(places);    // Prints ‘New York‘

简单的改变之后,我们没能得到正确的结果!如果仔细阅追踪源代码,可能已经发现问题了:List<T> 是可变数据结构,函数PrintMultiWord 在调用RemoveAll 时,意外地修改了它;当我们在代码的后面调用PrintLongest 时,集合places 中只包含了一项,即“NewYork”。现在,我们看看如果使用不可变数据结构,为什么就不会出现类似的错误。

时间: 2024-08-02 08:06:33

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

2.2.2 使用不可变数据结构

函数程序表示数据,使用数据结构,我们会在第五.七章讨论数据结构.虽然数据结构的概念通常更简单,但是,我们现在要讨论复合数据类型,比如 C# 值类型,或类.从第一章我们知道,函数编程中的数据结构是不可变的. 不可变数据结构的概念,逻辑上可以从不可变值绑定的概念推导出来.典型的数据结构包含字段声明.如果我们不可变性的概念从变量声明扩展到字段声明,就能得出一切都是不可变的.在 C# 中,不可变类字段要使用 readonly 限定符,而 F# 中所有的数据结构都是不可变的,是默认情况.F# 并不是严格的

11.1.2.2 使用不可变数据结构

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

C++11中的Tuple和可变参数模版

C++11中的tuple是一个n元的可变元组,它相当于有n个元素的结构体,只不过这个结构体的成员都是匿名的,tuple中提供了一个get()方法来获取某个下标对应的元素的值.另外可以通过make_tuple()方法来构造一个tuple对象.具体用法如下 我们知道tuple中的元素个数是不确定的,而每个元素的类型通过模板参数指定,那么tuple是如何做到这些的呢?答案就是使用可变参数模板.在C++中,我们使用过printf函数,它的参数就是可变的,在C++11中也允许模板的参数也是可变的.举个例子

我的java学习笔记(11)关于装箱、参数可变方法和枚举类型

1.所有的基本类型都有一个与之对应的类,通常这些类称之为包装器. 2.对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值.对象包装器还是final,因此不能定义它们的子类. 3.假设定义一个整型数组列表,而尖括号中的类型参数不允许是基本类型,即不允许ArrayList<int>,这里可以使用Integer对象包装器类. Array<Integer> l = new ArrayList<>(); 4.装箱和拆箱是编译器认可的,而不是虚拟机.编译器在生成

有用函数编程

<序> 感谢 关于本书 关于封面 第一部分 学习函数式思维 第一章 不同的思维 1.1 什么是函数式编程? 1.2 通往有用函数编程之路 1.3 用函数式编程提高生产力 1.3.1 函数范式 1.3.2 声明式编程风格 1.3.3 了解程序的执行 1.3.4 设计并发友好的应用程序 1.3.5 函数风格怎样形成代码 1.4 函数式编程演示样例 1.4.1 用声明式风格表达意图 1.4.1.1 用 LINQ 处理数据 1.4.1.2 用 XAML 描写叙述用户界面 1.4.1.3 声明式函数动画

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++中的可变参数模板

作者:Eli Bendersky http://eli.thegreenplace.net/2014/variadic-templates-in-c/ 回到C++11前夜,编写带有任意参数函数的唯一办法是使用可变参数函数,像printf,使用省略号语法(-)以及伴随的va_族的宏.如果你曾经使用这个方法编写代码,你会知道这有多累赘.除了变成类型不安全外(所有的类型解析必须在运行时在va_arg里通过显式转换来完成),要做对并不容易.Va_宏执行低级的内存操作,我看见过许多因为没有小心使用它们导致

34 个今年11月最受欢迎的 JavaScript 库

作者:Iren Korkishko 译者:前端小智 来源:dev 点赞再看,养成习惯 本文 GitHub:github.com/qq449245884… 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料.欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西. 直接开门见山,看看有哪些好用受欢迎的库值得我们使用. 1.nodemon GitHub: github.com/remy/nodemo… GitHub Stars: 19.6 k 在编写调试Node.js项

javaweb学习总结二(静态导入、自动拆装箱、增强for与可变参数)

一:静态导入语法:import static 导入类.成员变量|成员方法那么在代码中可以直接使用变量或者方法,而不需要通过类名.来调用 静态导入: 1 import static java.lang.System.out; 那么在代码中直接调用变量: 1 @Test 2 //测试静态导入 3 public void testStaticImport() { 4 out.println("static import..."); 5 } 二:装箱或者拆箱装箱:将基本数据类型赋值给引用数据类