版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第1章使用C#开发搜索引擎快速入门使用C#开发搜索引擎快速入门搜索引擎经过最近几十年的快速发展,已经改变了人们的记忆方式。有研究表明,人们会忘记自己能在网络上找到的信息,而记住自己认为无法在网络上找到的信息。研究也发现,人们更容易记住在互联网的何处能找到这些信息,而不是记住信息内容本身。从某种意义上讲,由于有了搜索引擎,我们才可以把一些记忆任务交给机器来完成。很多网站需要开发搜索功能。不仅如此,学会自己开发搜索引擎还将会为解决很多问题提供一种新方法。本书介绍使用流行的.NET(C#)编程语言开发搜索引擎。一件事情有更多人参与,就更容易做好。为了更好的协作,本章介绍的搜索引擎大部分采用开源软件实
2、现。读者可以与猎兔搜索专业的技术开发人员一起改进相关实现。制作过程中所用的程序在所赠光盘中都能找到。本章首先介绍搜索引擎的应用现状,然后介绍搜索引擎整体结构,并深入展开分析搜索的基本技术,最后复习下C#编程基础。1.1 各种搜索引擎搜索引擎有运行在大规模云计算的通用搜索引擎,也有一些行业搜索以及网站搜索。通用搜索引擎是大颚,每一只都有自己独立的领地。行业搜索是领头雁,是各行业的旗帜。而网站搜索则像一只只小麻雀,麻雀虽小,五脏俱全。1.1.1 通用搜索目前通用搜索引擎的组织方式主要有网络综合搜索引擎和网络主题资源搜索引擎两种。其中网络综合搜索引擎能够广泛地采集各个互联网站点资源,并对其进行页面搜
3、索,将索引结果存入索引数据库,供网络用户检索,提供互联网网络资源地导航功能的工具,如Google、百度等。这样的公司需要大量的服务器和专业开发人员,运营开销大。解决经济上可行性就是一个问题。通用搜索引擎的主要收入是在搜索结果页中展示与用户输入的关键词相关的广告。条幅广告更早出现。按点击付费的关键词广告比条幅广告的收费额度更低。点击一次广告可能只收几分钱,而条幅广告的计价单位至少在几百块(人民币)以上。那些曾经被忽视的中小企业,一度被认为是游离在广告市场之外的客户,突然成了时代的宠儿。地球上最大的动物鲸鱼吃的是小鱼小虾,只有这样才能摄入足够的食物。通用搜索引擎企业是资本密集型企业,这样的公司往往
4、前期有风险投资,有一定盈利后成为上市公司。1.1.2 垂直搜索垂直搜索是针对某一个行业的专业搜索引擎,例如搜房(/),生活信息搜索(),职位搜索(),39健康网上的搜索。垂直搜索是搜索引擎的细分和延伸,是对网页库中的某类专门的重要数据进行处理后,再对信息进行一次整合,定向分字段抽取出需种形式返回给用户。垂直搜索需要从茫茫的互联网中获取行业信息,信息按行业过滤和分类是必不可少的。垂直搜索引擎和普通的网页搜索引擎的另一个最大区别是对网页信息进行了结构化信息抽取,也就是将网页的非结构
5、化数据抽取成特定的结构化信息数据,比如网页搜索是以网页为最小单位,基于视觉的网页块分析是以网页块为最小单位,而垂直搜索是以结构化数据为最小单位。然后将这些数据存储到数据库,进行深一步的加工处理,如去重、分类等,最后分词、索引再以搜索的方式满足用户的需求。整个过程中,数据由非结构化数据抽取成结构化数据,经过深度加工处理后以非结构化的方式和结构化的方式返回给用户。垂直搜索引擎的应用方向很多,比如企业库搜索、供求信息搜索引擎、购物搜索、房产搜索、人才搜索、地图搜索、mp3搜索、图片搜索几乎各行各业各类信息都可以进一步细化成各类的垂直搜索引擎。垂直搜索引擎大体上需要以下技术:(1)定向的网络爬虫;(2
6、)网页结构化信息抽取技术或元数据采集技术;(3)中文分词、全文检索;(4)其他信息处理技术。垂直搜索引擎的技术评估应从以下几点来判断:(1)全面性:应该能从众多的来源采集信息。(2)更新性:用户最好可以在几秒钟或几分钟内看到最新发布的信息。(3)准确性:数据分类准确,不能包含重复冗余信息。(4)功能性:功能完善,可以同时搜索文字信息,图片,视频,地理信息等。垂直搜索的进入门槛很低,但是竞争的门槛很高。没有专注的精神和精湛的技术是不行的。行业门户网站具备行业优势但它们却没有技术优势,绝对不要想象着招几个人就可以搞定垂直搜索的全部技术。作为一个需要持续改进可运营的产品而不是一个项目,对技术的把握控
7、制程度又是垂直搜索成功的重要因素之一。与专业的搜索技术提供商合作共赢是一种现实的解决方法。其中猎兔搜索是专业提供基于Lucene和自然语言处理商业支持的企业搜索公司。1.1.3 站内搜索站内搜索有三种流行的实现方式:q 基于数据库的搜索 比如SQL Server或者MySQL内部都有对全文检索列的支持。q 基于爬虫抓取的站内搜索 Google通过从外部抓取网页的方式提供免费的站内搜索。q 站内搜索软件系统 通过和数据库的同步利用Lucene建立独立的全文索引的站内搜索系统。真正的全文检索应具备相关性排序技术和分词索引功能。分词、索引、排序这是全文检索的基本和核心,缺一不可。全文检索至少需要具备
8、中文分词、索引、相关性排序功能。所以简单考查一个站内搜索引擎的真伪只需要知道:能否实现相关性排序、国际标准的搜索语法、动态摘要、飘红、支持海量数据多并发快速查询、搜索耗时极短。猎兔(/)企业搜索的实现正是这样一种站内全文搜索的实现。1.2 搜索引擎整体结构一个最简单的搜索引擎由搜索和抓取两部分组成,完整的结构如图1-1所示。图1-1 搜索引擎的基本结构数据来源可以是Web或者数据库等,也可以是本地路径等。如果来源于互联网,就要用到爬虫。抓下来的文本在写入索引库时,需要切分成一个个的词。1.3 搜索引擎基本技术一个基本的搜索包括采集数据的爬虫和索引库的管理以
9、及搜索页面展现等部分。本书的第2章到第6章将分别详细地介绍这些内容。1.3.1 网络爬虫网络爬虫(Crawler)的主要作用是获取互联网上的信息。网络爬虫利用主页中的超文本链接遍历Web,通过URL引用从一个HTML文档爬行到另一个HTML文档。是整个互联网抓取的入口。网络爬虫收集到的信息可有多种用途,如建立索引、HTML文件的验证、URL链接验证、获取更新信息、站点镜像等。网络爬虫建立的页面数据库,包含有根据页面内容生成的文摘,这是一个重要特色。网站本身可以声明不希望被搜索引擎收入的内容。这可以有两种方式实现:第一种方式是在站点增加一个纯文本文件,例如http:/
10、/robots.txt;另外一种方式是直接在HTML页面中使用robots的meta标签。在抓取网页时大部分网络机器人会遵循Robot.txt协议。1.3.2 文本挖掘搜索文本信息需要理解人类的自然语言。文本挖掘指从大量文本数据中抽取隐含的、未知的、可能有用的信息。常用的文本挖掘方法包括:全文检索、中文分词、句法分析、文本分类、文本聚类、关键词提取、文本摘要、信息提取、智能问答等。文本挖掘相关技术的结构如图1-2所示。图1-2 文本挖掘的结构1.3.3 全文索引早在计算机出现之前,为了方便查询,已经出现了人工为图书建立的索引,比如图1-3中的名词索引。为了按词快速定位
11、抓取过来的文档,需要以词为基础建立全文索引,也叫倒排索引(Inverted index),如图1-4所示。倒排索引是相对于正向索引来说的,首先用正向索引来存储每个文档对应的单词列表,然后再建立倒排索引,根据单词来索引文档编号。图1-3 人工建立的名词索引图1-4 以词为基础的全文索引例如要索引如下两个文档:Doc Id 1:自己动手写搜索引擎Doc Id 2:自己动手写网络爬虫首先把这些文档中的内容分成一个个的单词:Doc Id 1:自己/动手/写/搜索引擎Doc Id 2:自己/动手/写/网络爬虫按单词建立的倒排索引结构如表1-1所示。表1-1 Lucene.Net中的倒排索引结构词(文档,
12、 频率)在文档中出现的位置动手( 1, 1 ), ( 2, 1 )( 2 ), ( 2 )搜索引擎( 1, 1 )( 4 )网络爬虫( 2, 1 )( 4 )写( 1, 1 ) ,( 2, 1 )( 3 ), ( 3 )自己( 1, 1 ),( 2, 1 )( 1 ), ( 1 )每个单词(term)后面的文档编号(docId)列表叫做posting list。在全文索引包Lucene.Net中,倒排索引结构存储在二进制格式的多个索引文件中,其中以tis为后缀的文件中包含了单词信息,frq后缀的文件记录单词的文档编号和这个单词在文档中出现了多少次,也就是频率信息,prx后缀的文件包含了单词出现
13、的位置信息。为了快速地查找单词,可以先对单词列表排序,例如,新华字典和现代汉语词典按拼音排序。查词典的时候要找一个词,可以把词典从头到尾翻看直到找到这个词为止。更快的方法是,直接翻看词典中间一页,看是否有这个词。如果比要找的词小,则往后找,如果比要找的词大,则往前找。这个方法叫做折半查找。从排好序的词表中查找一个词可以采用折半查找的方法快速查询。“购物街”节目,里面猜价格那帮人真让人痛苦。“500”“高了”“400”“高了”“300”“高了”“200”“低了”“210”“低了”“220”“低了”这个故事告诉我们学会折半查找多么重要。选择一个从0100的数字,并且依赖你的猜测,我说:对了,高了或
14、者低了。你选择从哪个开始猜呢?50当然!为什么?最好的情况是你猜中了。最差的情况是,你被告知 “高了”或者“低了”。现在思考下,如果你被告知“高了”,你就消除了51100。如果你被告知“低了”,你就消除了049,换句话说,你消除了一半的可能性。505174757699100如果假设是“低了”, 接下来你猜什么呢? 75。因为它位于50100。如果你还没猜对, 你又消除了一半的可能性!一个上限值和下限值界定查询词所在的区间范围。下面是实现折半查找的代码。/输入排好序的数组和待查询的值,返回在数组中的位置static int BinarySearch(int array, int value) i
15、nt low = 0; /查询的开始范围 int high = array.Length 1; /查询的结束范围 int midpoint = 0; /中间点 while (low = high) midpoint = (low + high) / 2; /检查是否和数组中的中间值相等 if (value = arraymidpoint) return midpoint; else if (value arraymidpoint) high = midpoint - 1; else low = midpoint + 1; /没有找到 return -1;1.3.4 搜索语法介绍专业的搜索引擎一
16、般都会实现一个搜索语法,基本的搜索语法有逻辑运算符:q 与(+、空格)q 或(OR、|)q 非(-)例如,搜“神雕侠侣”,希望是关于武侠小说方面的内容,却发现很多关于电视剧方面的网页。那么就可以这样查询:神雕侠侣 -电视剧 注意,前一个关键词与减号之间必须有空格,否则,减号会被当成连字符处理,而失去减号语法功能。减号和后一个关键词之间,有无空格均可。除了逻辑运算相关的搜索语法,还有:(1)把搜索范围限定在网页标题中intitle网页标题通常是对网页内容提纲挈领式的归纳。把查询内容范围限定在网页标题中,有时能获得良好的效果。使用的方式,是把查询内容中,特别关键的部分,用“intitle:”领起来
17、。例如,找小沈阳的小品,就可以这样查询:小品 intitle:小沈阳注意,intitle:和后面的关键词之间,不要有空格。(2)把搜索范围限定在特定站点中site有时候,如果知道某个站点中有自己需要找的东西,就可以把搜索范围限定在这个站点中,提高查询效率。使用的方式,是在查询内容的后面,加上“site:站点域名”。例如,要从天空网下载软件查找msn聊天工具,就可以这样查询:msn site:。注意,“site:”后面跟的站点域名,不要带“http:/”;另外,site:和站点名之间,不要带 空格。site语法的另外一个用处是查看一个网站被搜索引擎收录的情况,例如通过site:
18、 可以看出Google中收录了26 800条瑞丽搜索的信息。1.3.5 搜索用户界面搜索用户界面一般由搜索首页,搜索结果显示页和高级搜索页面组成。图1-5是一个搜索首页,包含一个搜索输入提示的效果:图1-5 搜索首页图1-6是一个搜索结果页面。图1-6 搜索结果页在搜索结果页会对搜索词高亮显示,在搜索输入框下面会给出相关搜索词列表。如果用户搜索错误,在搜索输入框下面会给出可能的查询词提示“您是不是要找:XXX”,如图1-7所示。图1-7 拼写检查图1-8是一个网站搜索的高级搜索界面。图1-8 高级搜索上面的界面中包含对“标题”,“内容”和“发布日期”多个字
19、段的联合查询,之间的关系是“与”的逻辑关系。1.4 C#开发快速入门首先从一个简单的搜索引擎入手,实现一个简单的指定目录文件的搜索引擎。实现之前需要有C#开发方面的知识。1.4.1 准备开发环境首先准备一个C#开发环境。当前使用Visual Studio .NET 2008/2010。Visual Studio .NET 2008可以到微软的网站/zh-cn/teamsystem/aa718766.aspx下载得到。C#源文件的扩展名为.cs,如爬虫类Crawler.cs,或者分词类Segmenter.cs。1.4.2 基本语法从语法上讲,C#语
20、言和Java语言非常相似,只是在细节上有一些差别。实际上,C#语言和Java语言的主要差别不是在语言本身,而是在它们所执行的平台上。Java程序需要一个运行环境JRE(Java Runtime Environment)来执行代码,但JRE只限于在Java这一门语言中使用。C#语言也需要一个运行环境CLR(Common Language Runtime),但是CLR提供了对多种语言的支持。Java源代码可以被编译成字节代码的一种中间状态,然后由已提供的虚拟机来执行这些字节代码。C#代码也被编译成一种中间状态,称为中间语言(IL),但是IL代码则被传输到由CLR管理的执行进程上,然后通过CLR的J
21、IT编译器编译成本地代码执行。C#语言与Java语言相比也有很多不同和改进之处。例如,C#语言的文件名不受文件中类名的限制,而在Java语言中则有此限制。另外,C#语言也提供了一些在Java语言中没有的功能,如运算符重载、封箱和装箱,以及方法隐藏等。C#语言和C+、Java语言一样,也区分大小写,不过其中的String和string对象可以 通用。和Java一样,C#每一个应用程序都应该有一个入口点,表明该程序从哪里开始执行。为了让系统能找到入口点,入口方法名规定为Main。class HelloWorld Public static void Main() System.Console.Wr
22、iteLine(Hello World); 由于.NET Framework包含差不多13 000个类,所以用命名空间来将其归类,便于查找。System就是一个命名空间。和Java语言中的import对应的是using关键词。例如:using System;那么,语句中就不需要加上命名空间前缀了,而可以简写为:Console.WriteLine(Hello World);使用.NET Framework中的类,必须做两件事情:首先应用程序必须引用包含这个类的程序集。然后应用程序必须导入这个类所关联的命名空间。C#中的一个项目最多只能定义一个可执行的类,不像Java,每个类都可以有一个main方
23、法,从而是可以执行的。但是往往需要有很多可以直接执行的测试代码。可以把这些功能不同的代码封装在不同的方法中,这样至少避免了大量的注释代码。有些方法处理的方式相同,只是输入参数不一样,可以把这些方法命名成同一个名称。这叫做方法重载。例如分词类可以切分一个字符串,或者一个文件。Public class Segmenter Public Split(String sentence) Public Split(File file) /内部调用Split(String) 在C# 3.0中增加了一个var关键字,可以用var来声明任意类型的局部变量。这叫做隐含类型。在C#语言中它仅仅是一个关键字,不代表一
24、种新的类型,它仅是负责告诉编译器,该变量需要根据初始化表达式来推断变量的类型,而且只能是局部变量。var声明变量之后,变量类型就确定下来了,不会再变。var关键字可以这样声明任意类型的局部变量:var name = csharp;var names = new string C#, Java ;等价于下面语句:string name = csharp;string names = new string C#, Java;1.4.3 多维数组有些动态规划算法把计算的中间结果存储在二维数组中。一条语句就可以创建一个一维数组,但是为二维数组分配空间则还需要一个循环。例如,创建一个存储概率的二维数组:
25、int stageLength = 10; /第一维的长度double prob = new doublestageLength;/二维数组for (int i = 0; i 8) & 0xff;异或运算取得二进制数组有差别的位:UInt32 val = x y;海明距离(HammingDistance)是针对长度相同的字符串或二进制数组而言的。对于二进制数组s和t,H(s, t)是两个数组对应位有差别的数量。例如:1011101 和 1001001 的海明距离是2。可以把两个无符号整型数按位异或(XOR),然后计算结果中1的个数,结果就是海明距离,见表1-2。表1-2 数字的十进制表示数字二
26、进制表示20000001050000010100000111计算两个数的海明距离:private UInt32 HammingDistance(UInt32 x, UInt32 y) UInt32 dist = 0, val = x y; /统计val中1的个数 while (val0) +dist; val &= val - 1; return dist;1.4.5 枚举类型词有名词或动词等类别,句子有陈述句或疑问句等类型。使用枚举比使用无格式的整数来描述这些类型至少有如下三个优势:q 枚举可以使代码更易于维护,有助于确保给变量指定合法的、期望的值。q 枚举使代码更清晰,允许用描述性的名称表
27、示整数值,而不是用含义模糊的数来表示。q 枚举使代码更易于键入。在给枚举类型的实例赋值时,VS.NET的集成开发环境会通过智能感知功能弹出一个包含可接受值的列表框,减少了按键次数,并能够让我们回忆起可能的值。可以把词的类型或句子的类型定义成枚举类型。public enum PartOfSpeech /词的类别 a,/形容词 n,/名词 v/动词 从枚举类型的名字得到值的方法:PartOfSpeech pos = (PartOfSpeech)Enum.Parse(typeof(PartOfSpeech),n);如果想知道有多少个可能的取值,可以使用:Enum.GetNames(typeof(Pa
28、rtOfSpeech).Length虽然也可以使用Enum.GetValues方法得到枚举类型中的项目,但是Enum.GetNames比Enum.GetValues快10倍以上。这是因为要对Enum.GetValues返回的整数执行装箱和拆箱操作。1.4.6 面向对象生活中,会把一些复杂的东西封装起来,例如将多种化学药品封装在一个胶囊中。软件往往由很多万行代码组成,也是一个复杂的系统,为了能够封装细节,需要抽象出对象。类就是对对象的定义。对象(Object)是类(Class)的一个实例(Instance)。如果将对象比作房子,那么类就是房子的设计图纸。在C#中,类往往就是一个.cs文件,对象就
29、是类代码到内存区域的一个映射。类的定义中包括属性和方法。内功深厚的武林高手出招往往平淡无奇。同理,编程高手也不会用奇门怪招写程序。良好的编程风格是产生高质量程序的前提。类名和方法名一般都是大写字母开头,而变量名则是小写字母开头。例如,文本中的一个词包括开始位置和结束位置。public class Token /对词的定义 public string term; /词 public int start; /开始位置 public int end; /结束位置可以通过构造方法初始化对象中的属性。构造方法与类同名,无返回值,也不需要用void关键字特别声明这一点。,在创建对象时调用构造方法。所有类都
30、有构造方法,如果不定义构造方法,则系统默认生成空的构造方法,若有定义的构造方法,那么默认的构造方法就会失效了。public class Token public string term; /词 public int start; /开始位置 public int end; /结束位置 public Token(string t,int s,int e) /构造方法 this.term = t; start = s; end = e; C#中的对象都是System.Object的子类,也就是说所有的对象都继承System.Object。这样的好处是所有的对象都可以有公用的方法,例如ToStrin
31、g()方法。Token t = new Token(hello,0,4);Console.WriteLine(t);System.Object默认提供的ToString()方法会返回类型的名称。这样的信息一般没有什么用处,所以需要重写Object的ToString()方法。public class Token public string term; /词 public int start; /开始位置 public int end; /结束位置 public Token(string t,int s,int e) /构造方法 this.term = t; start = s; end = e;
32、 public override string ToString() /用于显示对象的内容 return this.term+:+this.start+:+this.end; 有的类只是用来存放数据,没有特别的方法,例如这里的Token类。把这样的类叫做POCO(Plain Old CLR Object)类。ref是传递参数的地址,out是返回值,两者有一定的相同之处,不过也有不同点。使用ref前必须对变量赋值,out不用。out的函数会清空变量,即使变量已经赋值也不行,退出函数时所有out引用的变量都要赋值,ref引用的可以修改,也可以不修改。例如,用在排序算法中的交换两个数的方法:/交换两
33、个变量的值,让前面一个数小,后面一个数大static void Swap(ref int a,ref int b) int c = 0; if (a b) c = a; a = b; b = c; static void Main(String args) int x = 6; int y = 4; Swap(ref x,ref y); Console.WriteLine(x 的值为:0,y的值为:1, x, y); /格式化输出 Console.ReadLine();1.4.7 集合类文档是单词的集合。搜索结果集也是文档的集合。所以在搜索引擎开发中,集合类必不可少。集合类有存储一列的动态数组
34、List和队列Queue及堆栈Stack,还有存储键值对的Dictionary和SortedList。Dictionary是散列表的实现,而SortedList则是按键排好序的数组。例如,使用Dictionary记录每个单词对应的频率:/记录单词和对应的频率Dictionary wordCounter = new Dictionary(); /加入重复键会引发异常wordCounter.Add(苹果,1);wordCounter.Add(桔子,2);wordCounter.Add(香蕉,3);.NET 3.5多了个HasSet用来存储集合。从名称可以看出,它是基于散列表的。但是只存储键,而不存
35、储值。作为键的数据类型要同时重写Equals方法和GetHashCode方法。如果我们定义的一个类型只重写了Equals而没有重写GetHashCode方法时编译器会发出警告信息。因为首先要调用GetHashCode得到散列码,两个不同对象的散列码相同,这时候就发生使用同一个槽的冲突。就好像去一个窗口办理业务,发现前面已经有人了,这时候需要在后面排队。如果有冲突,再调用Equals方法判断是否同一个对象。需要知道集合中有多少个元素,可以使用ICollection中定义的Count方法。所有集合类都实现了这个接口。动态数组List是最常见的一个集合类。可以通过List.Count知道数组的 长度
36、。在生活中,一般把同一个容器存放同一类东西。例如,光盘盒专门用来放光盘,糖果盒专门用来放糖果。可以使用泛型来检查集合中存储的数据类型。例如,List指定放字符串。List words = new List(); /存放字符串的动态数组words.Add(Hello);words.Add(World);Console.WriteLine(words.Count); /集合中有2个元素支持泛型的集合类位于System.Collections.Generic命名空间。有些接口或类有泛型和非泛型两个版本,一般来说,应该尽量使用泛型。例如使用IComparable而不使用IComparable,这样可以
37、避免值类型的装箱和取消装箱操作。对于支持IEnumerable接口的集合类,可以使用foreach遍历集合中的元素:List words = new List(new string3 北京, 上海, 深圳);foreach (string word in words)/用foreach循环遍历元素 Console.WriteLine(word);words.RemoveAt(0); /删除最开始的元素如果需要自己写一个支持IEnumerable接口的集合类,就需要实现GetEnumerator方法返回一个迭代器(Enumerator)。在迭代器中经常会用到yield,用于向枚举数对象提供值或发
38、出迭代结束信号。它的形式为下列之一:yield return ; /提供值yield break; /发出迭代结束信号例如,遍历链表:public IEnumerator GetEnumerator() Node current = head; /取得链表的头 while (current != null) yield return current.item; current = current.next; /取得下一个元素 IEnumerator IEnumerable.GetEnumerator() return this.GetEnumerator();IComparable是一个用来比
39、较当前对象和同一类型的另一对象的接口。其中的成员方法有:CompareTo。例如一个可比较权重的单词类:public class WordWeight : IComparable public String word; public double weight; /重要度 public Int32 CompareTo(WordWeight o) if (this.weight = o.weight) return 0; return (this.weight o.weight) ? -1 : +1); IComparer也是一个比较大小的接口,不过是比较传入的两个对象。其中的成员方法有:Com
40、pare比较两个对象并返回一个值,指示一个对象是小于、等于还是大于另一个对象。例如比较两个单词的重要度:class CompareWord : System.Collections.Generic.IComparer public Int32 Compare(WordWeight o1, WordWeight o2) double i1 = o1.weight; double i2 = o2.weight; if (i1 = i2) return 0; return (i1 i2) ? -1 : +1); 当一个对象需要按不止一种方式排序时,往往使用IComparer。使用CompareWor
41、d的 例子:/初始化对象的数组WordWeight fullResults = new WordWeightnew WordWeight(北京,4), new WordWeight(中国,5) ;/按权重排序System.Array.Sort(fullResults, new CompareWord();1.4.8 泛型假设需要让存储的值是某一种确定的数据类型,但不要求只能是字符串或者整数类型。例如,节点中的值类型不固定:public class TrieNode private char nodeKey; private T nodeValue; private Boolean termin
42、al; private Dictionarychar, TrieNode children = new Dictionarychar, TrieNode();使用TrieNode的例子:TrieNode stringTrie = new TrieNode(); /让 nodeValue是String类型1.4.9 委托和事件假设有同一类的几个方法,输入和输出参数都相同,这些方法可以互换使用。委托就是把方法当作一个变量。用delegate关键字来修饰方法变量。例如,有一个单词类:class Token String term; /词 int freq; /词频有个单词组成的数组。在折半查找时,需
43、要按词排序,而要取得最高频的几个单词,则需要按频率排序。所以需要有两个版本的比较器:CompareTerm按词比较;CompareFreq按词频比较。public static int CompareTerm(Token token1, Token token2) /按词比较Token大小 string n1 = token1.term; string n2 = token2.term; if (String.Compare(n1, n2) 0) return 1; else if (String.Compare(n1, n2) 0) return -1; else return 0; pub
44、lic static int CompareFreq(Token token1, Token token2) /按词频比较Token大小 int n1 = token1.freq; int n2 = token2.freq; return (n1 - n2);/声明比较器,这里是一个委托/声明委托类似于方法的定义,但没有方法体,定义的前面加上delegate关键字public delegate int Comparer(Token obj1, Token obj2);class SimpleDelegate Token tokens = new Token5; public SimpleDel
45、egate() tokens0 = new Token(Joe, 2); tokens1 = new Token(John, 4); tokens2 = new Token(Jane, 1); tokens3 = new Token(John, 3); tokens4 = new Token(Jack, 6); static void Main(string args) SimpleDelegate sd = new SimpleDelegate(); /这是委托实例 Comparer cmp = new Comparer(Token.CompareTerm); Console.WriteLi
46、ne(n排序前: n); sd.PrintNames(); /观察委托参数 sd.Sort(cmp); Console.WriteLine(n排序后: n); sd.PrintTokens(); /观察委托参数 public void Sort(Comparer compare) Token temp; for (int i=0; i tokens.Length; i+) for (int j=i; j 0 ) temp = tokensi; tokensi = tokensj; tokensj = temp; public void PrintTokens() Console.WriteLine(Tokens: n); foreach (Token token in tokens) Console.WriteLine(token.ToString(); 就好像发出的邮件可以抄送到多个邮箱一样。一个委托变量可以指向多于一个方法,也就是支持多播(Multicast)。多播委托是一个派生于System.MulticastDelegate的类
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年珠宝行业洞察报告及未来五至十年发展趋势报告
- 2026年型煤型焦行业洞察报告及未来五至十年发展趋势报告
- 2026年合肥市产业投资控股集团有限公司人员招聘考试参考试题及详细答案解析
- 2026年深圳市资本运营集团有限公司人员招聘考试题库及详细答案解析
- 汽车维修技师汽车电路诊断步骤指引
- 2026年舱底油水分离器行业洞察报告及未来五至十年发展趋势报告
- 2026年中石化宁波工程公司人员招聘笔试参考试题及详细答案解析
- 2026年其他体育组织行业投资研究报告及未来五至十年增长动能与投资价值分析
- 2026年苏教版小学语文三年级上册第11单元阅读理解专项训练习题及答案
- 2026年北师大版高中化学选修第二册第4章有机化学基础测试卷及答案
- 腹部手术术后腹胀的护理措施
- 初中道德与法治综合活动题专题教学设计:陕西中考真题归类与情境化问题解决路径
- 2026年中考名著阅读《简爱》练习附答案试题
- 2025年下半年信息安全工程师《案例分析》真题及答案解析
- 2026苏教版新教材解读三、四年级上册数学编排变化剖析
- 2026秋季苏教版(新教材)小学科学六年级上册(全册)每课课时练习及答案(附目录p97)
- 中央安全生产考核巡查组建筑施工领域重大事故隐患汇编
- 2026年脑机接口神经信号解码项目商业计划书
- 2026年高考物理全国一卷真题卷附答案
- 公共政策不确定性管理:框架、模型与实践
- 2026极地勘探装备专用润滑油研发难点与极端环境测试数据
评论
0/150
提交评论