我们已经学习了怎样创建一个简单的Monad, MaybeMonad, 并且知道了它如何通过在 Bind函数里封装处理空值的逻辑来移除样板式代码. 正如之前所说的,我们可以在Bind函数中封装更复杂的逻辑. 下面给出一个更复杂更典型的Monad例子,一个解析器Monad. 在本篇将要介绍一个解析器,在之后的篇幅里将会把解析器转换成一个 Monad.

首先我们思考解析器要完成什么功能,它接受一个输入,通常是一些文本,然后输出期望的结果. 因此一个CSV解析器将会接受一个文本文件,输出行和列的数据,并带有数据类型. 我们可以把parser抽象成一个函数 ,它接受一个string,返回某种类型 T:

Func<string,T>

将一个大的任务分解成小的任务实现通常会更简单,所以如果我们能同坐组合很多小的解析器来构建我们的解析器会更好. 每个小的解析器可能会消耗部分字符串,所以我们可以定义函数接受一个 string, 返回 T 和匹配后剩余的字符串

Func<string, Tuple<T,string>>

Tuple是在.Net4里引入的新类型, 你可以用自定义类型替代。

我们的解析器可能并不能正确解析输入的字符串,因此我们还需要能处理解析失败的情况, 这里可以使用我们已定义的 Maybe 类型

Func<string, Mayb<Tuble<T,string>>>

现在来创建一个比较简单的解析器, 它匹配字符串"Hello":

public static Maybe<Tuple<T,string>>FindHello(string input)
{
return input.StartWith("hello") ?new Just<Tuple<string,string>>(Tuple.Create("Hello",input.Skip("Hello".Length).AsString())) :(Maybe<Tuple<string,string>>)new Nothing<Tuple<string,string>>();
}

如果输入的字符串中包含"Hello",将会返回"Hello"和剩余的字符串

var result=Parsers.FindHello("Hello world");

var justResult= result as Just<Tuple<string,string>>;

Console.WriteLine("justResult.Value.Item1={0}",justResult.Value.Item1);

//justResult.Value.Item1=Hello

Console.WriteLine("justResult.Value.Item2={2}",justResult.Value.Item2);

//justResult.Value.Item2=World

如果我们输入"GoodBye" ,它将会返回Nothing:

var result2=Parsers.FindHello("Goodbye world");

Console.WriteLine("resulte2={0}",result2);

//result2=Nothing

通过创建一个可以解析任何字符串的解析器工厂,我们可以使我们的解析器更优美, 首先定义一个delegate:

public delegate Maybe<Tuple<T,string>>Parser<T>(string input)

现在定义Find,写在扩展方法里:

public static Parser<string>Find(this string stringToFind)

{
return input=> input.StartsWith(stirngToFind) ?new Just<Tuple<string,string>>(Tuple.Create(stringToFind,input.Skip(stringToFind.Length).AsString())) :(Maybe<Tuple<string,string>>)new Nothing<Tuple<string,string>>();
}

这是一个高阶函数,它返回一个函数,就是我们的解析器, 注意我们的解析器是一个delegate。

现在我们可以用它创建一些解析器,比如一个"Hello" 解析器和一个"World"解析器

var helloParser= "Hello".Find();

var worldParser="World".Find();

我们加一个扩展方法方便把解析结果转换成string:

public static string AsString<T>(this Maybe<Tuple<T,string>>parseResult, Func<T,string>unwrap)
{
var justParseResult= parseResult as Just<Tuple<T,string>>; return (justParseResult != null ?unWrap(justParseResult.Value.Item1)) :"Nothing";
}

现在我们用我们的helloParser 和 worldParser来解析字符串 :

var result =helloParser("Hello World").AsString(s=>s);

Console.WriteLine("result = {0}", result);

//result = Hello

var result2= worldParser("World Hello").AsString(s=>s);

Console.WriteLine("result2={0}",result2);

//result=World

我们怎么能把这两个parser结合起来创建一个"HelloWorld"的parser呢,这里有一个生硬的实现:

Parser<Tuple<string,string>>helloWorldParser=input=>
{
var helloResult= helloParser(input) as Just<Tuple<string,string>>; if(helloResult == null) return new Noting<Tuple<Tuple<string,string>,string>>(); var worldResult= worldParser(helloResult.Value.Item2) as Just<Tuple<strin,string>>; if(worldResult == null) return new Noting<Tuple<Tuple<string,string>,string>>(); return new Just<Tuple<Tuple<string,string>,string>>(Tuple.Create( Tuple.Create(helloResult.Value.Item1,worldResult.Value.Item1),worldResult.Value.Item2)); }; var result3=helloWorldParser("HelloWorld").AsString(s=>s.Item1 + " " + s.Item2); Console.WriteLine("result3 = {0}",result3); //result=Hello World

这样写非常繁琐,假如我们要组合更复杂的解析器,比如CSV解析器,将会非常令人头疼.但是不要担心,在下一篇我们将会把我们的解析器转换成Monad, 并以非常简单的方式组合他们

介绍一个简单的Parser的更多相关文章

  1. 安全小测试:介绍一个简单web安全知识测试的网站

    https://websecurity.firebaseapp.com/ 一次测试一共7道题,最后有答案,可以反复做,每次随机抽题

  2. 自己动手实现一个简单的JSON解析器

    1. 背景 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着诸多优点.比如易读性更好,占用空间更少等.在 ...

  3. Web开发之tomcat配置及使用(环境变量设置及测试,一个简单的web应用实例)

    Tomcat的配置及测试: 第一步:下载tomcat,然后解压到任意盘符 第二步:配置系统环境变量 tomcat解压到的D盘 (路径为: D:\tomcat), 配置环境变量: 启动tomcat需要两 ...

  4. 一个简单的基于 DirectShow 的播放器 1(封装类)

    DirectShow最主要的功能就是播放视频,在这里介绍一个简单的基于DirectShow的播放器的例子,是用MFC做的,今后有机会可以基于该播放器开发更复杂的播放器软件. 注:该例子取自于<D ...

  5. UE4学习心得:Scene Component蓝图的一个简单应用

    Scene Component是蓝图类中一个不怎么常用的分类(特别是对于新手而言),主要是其实现的功能可以在Actor类中用相同的方法实现,使其作用显得有点多余. 笔者在使用过这个类之后发现其作用更相 ...

  6. JMS学习(四)-一个简单的聊天应用程序分析

    一,介绍 本文介绍一个简单的聊天应用程序:生产者将消息发送到Topic上,然后由ActiveMQ将该消息Push给订阅了该Topic的消费者.示例程序来自于<JAVA 消息服务--第二版 Mar ...

  7. 《深度解析Tomcat》 第一章 一个简单的Web服务器

    本章介绍Java Web服务器是如何运行的.从中可以知道Tomcat是如何工作的. 基于Java的Web服务器会使用java.net.Socket类和java.net.ServerSocket类这两个 ...

  8. Matlab高级教程_第二篇:一个简单的混编例子

    1. 常用的混编是MATLAB和VS两个编辑器之间的混编方式. 2. 因为MATLAB的核是C型语言,因此常见的混编方式是MATLAB和C型语言的混编. 3. 这里介绍一个简单的MATLAB语言混编成 ...

  9. 用c#自己实现一个简单的JSON解析器

    一.JSON格式介绍 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.相对于另一种数据交换格式 XML,JSON 有着很多优点.例如易读性更好,占用空间更 ...

随机推荐

  1. dataGridView 设置

    //窗体加载事件 //内容居中 dataGridView1.RowsDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCe ...

  2. jquery相关常用的工具函数

    1.弹出提示框: function prompt(msg){ $("<div>" + msg + "</div>").css({ &qu ...

  3. BZOJ 1688: [Usaco2005 Open]Disease Manangement 疾病管理 状压DP + 二进制 + 骚操作

    #include <bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) #defin ...

  4. Idea中修改servlet模板

    1.点击左上角的File: Setting --> Editor --> File and Code Templates --> Other --> web -->Ser ...

  5. Context、Select(day01)

    Oracle sql: 4天 plsql: 2天 proc: 2天 数据库介绍 1.1 数据库简介 1.1.1 数据管理技术的发展 人工管理阶段:20世纪50年代中期之前 文件管理阶段:20世纪的50 ...

  6. 【Educational Codeforces Round 53 (Rated for Div. 2) C】Vasya and Robot

    [链接] 我是链接,点我呀:) [题意] [题解] 如果|x|+|y|>n 显然.从(0,0)根本就没法到(x,y) 但|x|+|y|<=n还不一定就能到达(x,y) 注意到,你每走一步路 ...

  7. 项目部署到tomcat出错(tomcat运行时的JDK版本)

    先展示一下错误,把项目部署到tomcat运行 出错原因 简单来说,就是执行代码的jdk版本 低于 编译的jdk版本 最后面的52.0是一种叫什么魔码,有各自对应的jdk版本. 其中52.0 对应的就是 ...

  8. Visible Trees

    Visible Trees Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Pr ...

  9. 0708关于理解mysql SQL执行顺序

    转自 http://www.jellythink.com/archives/924,博客比价清晰 我理解上文的是SQL执行顺序 总体方案.当你加入索引了以后,其实他的执行计划是有细微的变化,比方说刚开 ...

  10. 【ACM】nyoj_103_A+BII_201307291022

    A+B Problem II时间限制:3000 ms  |  内存限制:65535 KB 难度:3描述 I have a very simple problem for you. Given two ...