C#函数式程序设计之惰性列表工具——迭代器
有效地处理数据时当今程序设计语言和框架的一个任务。.NET拥有一个精心构建的集合类系统,它利用迭代器的功能实现对数据的顺序访问。
惰性枚举是一个迭代方法,其核心思想是只在需要的时候才去读取数据。这个思想保证了任何迭代算法都十分有效,同时又可以灵活地根据需要读取任意多的数据,而且不会造成过多的开销。
C#函数式程序设计之枚举元素
.NET集合类型的基础是一个名为IEnumberable的接口,以下就是这个接口的声明:
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
实际上IEnumberable接口只允许程序员做一件事:查询类的另一个接口IEnumerator:
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
下面这个类事实上是根据IEnumberable和IEnumerator接口声明的迭代模式的最简实现形式:
public class EndlessListWithoutInterfaces
{
public EndlessListWithoutInterfaces GetEnumerator()
{
return this;
} public bool NoveNext()
{
return true;
} public object Current
{
get
{ return "Something"; }
}
}
在EndlessListWithoutInterfaces类中也可以使用C#的foreach结构:
var list = new EndlessListWithoutInterfaces();
foreach (var item in list)
{
Console.WriteLine(item);
}
此实现极其基本,因此这将导致一个无限循环。
下面是EndlessList类的实现,这个类使用IEnumerator和IEnumberable两个类,这两个类相互嵌套,尽管它们不需要里外嵌套。但实际上,在IEnumberable类中嵌套IEnumerator类是一个常见模式:
public class EndlessList:IEnumerable
{
public class Enumerator:IEnumerator
{
int val = -1; public object Current
{
get
{ return val; }
} public bool NoveNext()
{
val++;
return true;
} public void Reset()
{
val = -1;
}
} public IEnumerator GetEnumerator()
{
return new Enumerator();
}
}
值序列的这种实现模式是非常灵活的,而且功能也强大。
C#函数式程序设计之迭代器函数的实现
C#2.0版本中引入了迭代器。它允许程序员创建IEnumerator/IEnumberable组合的实现,而无须手动实现其中的任意一个接口。除了支持非泛型接口外,它还支持泛型接口,因此可以只实现IEnumerator。
通常为了应用这个功能,只需要定义一个返回某个类型值的函数。编译器为了使用它的转换操作而采用的第二个准则是在函数中至少使用几个特殊关键字中的一个。最常用的是yield return语句。例如,前面的EndlessList例子可以由如下的一个C#迭代器实现:
public static IEnumerable<int> EndlessListFunc()
{
int val = 0;
while (true)
yield return val++;
}
此函数的返回类型是一个IEnumberable<int>,因此在原本使用一个能实现此接口的类实例的地方现在可以使用这个函数。下面几个语句可以迭代访问EndlessListFunc序列:
var list = EndlessListFunc();
foreach (var item in list)
{
Console.WriteLine(item);
}
IEnumerator/IEnumberable两个接口使用的整个声明模式与惰性有关,即只是在需要时才读取数据。这种模式的优点是:一个序列只是整个算法的一个很少但非常灵活的部分,序列也无需对它的将来使用进行任何假设。
下面这段代码是一个迭代器的实际例子,利用Twitter流的一个搜索,从Web服务读取数据。从Web服务读取数据也采用惰性方法,因为它使用了自动分页技术。迭代器根据需要读取新页,把长度未知的字符串序列返回给调用者。调用者按照自己喜欢的方式处理这个序列:
private static void TwitterSearchIterator()
{
foreach (var tweet in GetTweets("#msdn").Take(10))
Console.WriteLine(tweet);
} private static IEnumerable<string> GetTweets(string searchString)
{
var url = "http://search.twitter.com/search.atom?q=";
int page = 1;
var escapedSearchString = searchString.Replace("@", "%40").Replace("#", "%23");
XNamespace ns = "http://www.w3.org/2005/Atom"; while (true)
{
var doc = XDocument.Load(String.Format("{0}{1} & page={2}", url, escapedSearchString, page));
var entries = doc.Root.Elements(ns + "entry");
if (entries.Count() == 0)
yield break;
foreach (var entry in entries)
yield return
entry.Element(ns+"author").Element(ns+"name").Value+": "+WebUtility.HtmlDecode(entry.Element(ns+"title").Value;
page++;
}
}
迭代器不仅可以返回前面例子使用的IEnumberable和IEnumberable<T>接口,还可以返回IEnumerator和IEnumerator<T>接口。
C#函数式程序设计之链式迭代器
很容易把函数形式的迭代器链接在一起,并通过它们创建复杂的处理流程。这个思想广泛用于LINQ,这是函数式程序设计一个最重要的概念。
前面介绍了EndlessListFunc迭代器,它可以返回一个无穷的整数值序列,处理序列的迭代器函数可以与这个迭代器一起使用。例如下例是Take函数的实现,它通过参数接受一个证书序列和一个计数值,且只返回源序列中的前几个元素:
public static IEnumerable<int> Take(int count, IEnumerable<int> source)
{
int used = 0;
foreach (var item in source)
if (count > used++)
yield return item;
else
yield break;
}
可以把Take与EndlessListFunc一起使用:
var FiveElements = Take(5, EndlessListFunc());
foreach (var item in FiveElements)
Console.WriteLine(item);
这个例子很好地说明了如何把迭代器当成模块使用。
用在链式迭代器中的第二种类型函数是使用实际内容的函数,比如执行数值计算。
C#函数式程序设计之惰性列表工具——迭代器的更多相关文章
- C#函数式程序设计之函数、委托和Lambda表达式
C#函数式程序设计之函数.委托和Lambda表达式 C#函数式程序设计之函数.委托和Lambda表达式 相信很多人都听说过函数式编程,提到函数式程序设计,脑海里涌现出来更多的是Lisp.Haske ...
- C#函数式程序设计之局部套用与部分应用
函数式设计的核心与函数的应用以及函数如何作为算法的基本模块有关.利用局部套用技术可以把所有函数看成是函数类的成员,这些函数只有一个形参,有了局部套用,才有部分应用.部分应用是使函数模块化成为可能的两个 ...
- C#函数式程序设计之用闭包封装数据
如果一个程序设计语言能够用高阶函数解决问题,则意味着数据作用域问题已十分突出.当函数可以当成参数和返回值在函数之间进行传递时,编译器利用闭包扩展变量的作用域,以保证随时能得到所需要的数据. C#函数式 ...
- C#函数式程序设计之泛型
Intellij修改archetype Plugin配置 2014-03-16 09:26 by 破狼, 204 阅读, 0 评论,收藏, 编辑 Maven archetype plugin为我们提供 ...
- C#函数式程序设计之泛型(上)
在面向对象语言中,我们可以编写一个元素为某个专用类型(可能需要为此创建一个ListElement)的List类,或者使用一个非常通用.允许添加任何类型元素的基类(在.NET中,首先想到的是System ...
- C#函数式程序设计之代码即数据
自3.5版本以来,.NET以及微软的.NET语言开始支持表达式树.它们为这些语言的某个特定子集提供了eval形式的求值功能.考虑下面这个简单的Lambda表达式: Func<int, int, ...
- C#函数式程序设计之泛型(下)
C#函数式程序设计之泛型(下) 每当使用泛型类型时,可以通过where字句对泛型添加约束: + 这个例子直观地声明了一个约束:类型T必须与ListItem<string>相匹配.泛型类 ...
- QMUI UI库 控件 弹窗 列表 工具类 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 基于 Generator 和 Iterator 的惰性列表
初识 Lazy List 如果有了解过 Haskell 的朋友,对下面的这些表达一定不陌生 repeat 1 -- => [1, 1, 1, 1, 1,...] cycle "abc& ...
随机推荐
- 阶段性总结(PHP-JSON)
PHP JSON 本节我们将为大家介绍如何使用 PHP 语言来编码和解码 JSON 对象. 在没有json编码和解码之前,我们 html前台 和 PHP后台 之间的数据传输只能用字符串的方式传输. 但 ...
- python学习笔记(十二):发送邮件
我们在开发程序的时候,有时候需要开发一些自动化的任务,执行完之后,将结果自动的发送一份邮件,python发送邮件使用smtplib模块,是一个标准包,直接import导入使用即可,代码如下: impo ...
- Python 模块 - jieba
安装 jieba pip3 install jieba jieba 支持三种分词模式: 精确模式:将句子最精确地切开,适合文本分析 全模式:把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不 ...
- IDA Pro 权威指南学习笔记(十三) - 基本代码转换
IDA提供的代码转换包括: 1.将数据转换为代码 2.将代码转换为数据 3.指定一个指令序列为函数 4.更改现有函数的起始或结束地址 5.更改指令操作数的显示格式 代码显示选项 通过 Options ...
- png格式图片转为svg格式图片
png格式图片转为svg格式图片 (2012-08-30 16:24:00) 转载▼ 标签: 杂谈 分类: linux 在ubuntu下将png格式的图片转换成svg格式步骤如下:1.安装 inksc ...
- Eclipse/jre/jdk/jvm
理清一下什么是 jre.jdk.jvm这几个容易混淆的东西. 直接在电脑下安装Eclipse,会提醒你缺少JVM. 1.JVM Java Virtual Machine(Java虚拟机)的缩写. 为了 ...
- VS调试时JSON格式文件无法加载
VS调试时JSON格式文件无法加载 报错: 解决:在项目中的web.config中进行配置,configuration节中添加以下部份: <system.webServer> <st ...
- angularjs之级联菜单
原理: 1.ng-options中val.id as val.name for val in cascading 将id的值赋给 mg-modelone2.在通过ng-change传给函数3.当一级下 ...
- 跟着太白老师学python day11 闭包 及在爬虫中的基本使用
闭包的基本概念: 闭包 内层函数对外层函数的变量(不包括全局变量)的引用,并返回,这样就形成了闭包 闭包的作用:当程序执行时,遇到了函数执行,它会在内存中开辟一个空间,如果这个函数内部形成了闭包, 那 ...
- Linux 登陆提示文字
/etc/issue是从本地登陆显示的信息 /etc/issue.net是从网络登陆显示的信息 /etc/motd内容由系统管理员确定,常用于通告信息,如计划关机时间的警告等 每次用户登录时,/etc ...