【F#从入门到实战】11. F#库FParsec入门

网友投稿 704 2022-05-30

欢迎大家来到【F#从入门到实战】,在这里我将分享关于F#编程语言的系列文章,带大家一起去学习和成长,并探索函数编程语言F#这个有趣的世界。所有文章都会结合示例代码和笔者的经验进行讲解,真心想把十余年的IT经验分享给大家,希望对您有所帮助,文章中也定有不足之处,请海涵!本系统文章将从F#基本语法入手,逐步通过自定义类型来实现数学表达式的各种常见解析操作,如对表达式进行求值、化简、展开、求导和求积分等。此系统博文也是了解和实现一个简易的计算机代数系统的基础。

下面给出【F#从入门到实战】系统专题文章的目录:

【F#从入门到实战】01. F#语言快速入门

【F#从入门到实战】02. F#数组常见用法

【F#从入门到实战】03. F#自定义操作符

【F#从入门到实战】04. F#5.0新特征总结

【F#从入门到实战】05. F#表达式求值

【F#从入门到实战】06. F#表达式化简

【F#从入门到实战】07. F#表达式展开

【F#从入门到实战】08. F#大整数阶乘

【F#从入门到实战】09. F#表达式求导

【F#从入门到实战】10. F#表达式积分

【F#从入门到实战】11. F#库FParsec入门

【F#从入门到实战】12. F#库FParsec解析表达式

【F#从入门到实战】13. F#库FParsec实现求导符号计算

【F#从入门到实战】14. F#实现分部积分法

下面将正式开始本文的介绍:

FParsec 是一个F#语言构建的解析器组合库,主要用于解析文本,并结构化输出,可以为形式语法实现递归下降文本解析器。根据官网的介绍,FParsec库的主要功能包括:

支持上下文相关的无限前瞻文法

自动生成、高度可读的错误消息

Unicode 支持

【F#从入门到实战】11. F#库FParsec入门

对非常大的文件的有效支持

一个可嵌入的、运行时可配置的运算符优先级解析器组件

一个简单、高效且易于扩展的 API

针对性能进行彻底优化的实现

全面的文档

一个宽松的开源许可证,源代码和二进制形式的 FParsec 库是在简化的 BSD 许可下分发

官网为 http://www.quanttec.com/fparsec/ ,可以从官网查看具体的用法。FParsec 源代码在 GitHub 上可以下载:github.com/stephan-tolksdorf/fparsec

FParsec 库的源代码用 C# 和 F# 编写的,包含两个DLL库:FParsec.dll和 FParsecCS.dll ,项目需要引用此DLL库。下面给出一些示例,用来直观的掌握如何使用这个解析器库:

首先,所有 FParsec 类型和模块都在FParsec命名空间中声明。该命名空间包含一些基本类和4个 F# 模块,即:

Primitives:包含基本类型定义和解析器组合器

CharParsers:包含字符、字符串和数字的解析器,以及将解析器应用于输入流的函数

Error:包含用于创建、处理和格式化解析器错误消息的类型和辅助函数

StaticMapping:包含将静态键值映射编译为优化函数的函数

因此如果要使用open FParsec库,则应该首先引入此库:

open FParsec

首先需要需要根据解析的对象,构建合适的解析器Parser,当然,FParsec库中的模块如 FParsec.Primitives和FParsec.CharParsers模块中包含了内置的解析器,如解析float类型的解析器pfloat,其定义为:

val pfloat: Parser

如果要想调用此解析器,并返回结果(ParserResult<'Result,unit>),则需要run来进行调用 ,其中的ParserResult定义为:

type ParserResult<'Result,'UserState>= | Success of 'Result * 'UserState * Position | Failure of string * ParserError * 'UserState

解析器pfloat示例为:

let f2 = run pfloat "1.25" printfn "%O" f2 //Success: 1.25

当然,为了方便的进行测试运行,可以构建一个test函数:

let test p str = match run p str with | Success(value, _, _) -> printfn "OK: %A" value | Failure(err, _, _) -> printfn "Fail: %s" err

用此test函数来运行pfloat解析器示例,如下所示:

//解析float类型 test pfloat "1.25" //OK: 1.25 test pfloat "1.25E" //Fail: Error ...

其中对文本"1.25E"解析会出现如下错误信息:

Fail: Error in Ln: 1 Col: 6 1.25E ^ Note: The error occurred at the end of the input stream. Expecting: decimal digit

如果这个数值字符中有空字符(如空格)会解析正确吗?可以看下面的示例:

//数值后面可以有空格字符 test pfloat "1.27 " //OK: 1.27 //数值前面不可以有空格字符 test pfloat " 1.28" //Fail: Error ..

下面介绍一下如何从括号中提取数值,示例如下:

//解析string类型,忽略空白 ' ', '\t', '\r' ', '\n' let pstr_ws s = pstring s .>> spaces let pfloat_ws = pfloat .>> spaces //()中间的解析出float let pfloatBetKK = pstr_ws "(" >>. pfloat_ws .>> pstr_ws ")" test pfloatBetKK "(2.6)" //OK: 2.6 test pfloatBetKK "(2.7 )" //OK: 2.7 test pfloatBetKK "( 2.8 ) " //OK: 2.8 test pfloatBetKK " ( 2.9 ) " //Fail: Error... 左括号有空格

另外,还可以对特定括号中的字符按照分隔符进行拆分并解析,具体示例如下:

let parray = pstr_ws "[" >>. sepBy pfloat_ws (pstr_ws ";") .>> pstr_ws "]" //OK: [1.0; 2.0; 3.0] test parray @"[ 1 ; 2 ; 3 ] "

有时候,我们解析字符时,希望跳过特定字符,具体示例如下:

//spaces >>. 左空白 .>> spaces 右空白 let pfloat_ws2 = spaces >>. pfloat .>> spaces let f s = (skipStringCI "" >>. pfloat_ws2) s test f " 1.0 " //OK:1.0

上述示例,则跳过字符串,且忽略大小写,从而提取出1.0

最后,这个解析器还可以构建更加复杂的逻辑,比如判断变量命名是否合法:

let identifier = let isIdentifierFirstChar c = isLetter c || c = '_' let isIdentifierChar c = isLetter c || isDigit c || c = '_' many1Satisfy2L isIdentifierFirstChar isIdentifierChar "identifier" .>> spaces // 忽略右空白 let id = spaces >>. identifier .>> spaces test id "_name" //OK: "_name" test id " _name "//OK: "_name" (* Fail: Error in Ln: 1 Col: 1 1_name ^ Expecting: identifier *) test id "1_name "

由上示例可知,_name是合法的变量名,而1_name以数字开头,则不合法。

面向对象编程

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【小程序】007 Promise与Async/Await
下一篇:Kuernetebs系列一:使用Kubespray部署Kubernetes集群
相关文章