2008年12月6日星期六

Write Yourself a Scheme in 48 Hours/Parsing (一)

前一章其实标题本来是"编译与运行"。但是这两步反而难度不高,看了教程就很容易能明白,所以我没有讨论它。 上一章里重点介绍了开发工具,因为Haskell的语法决定,我们需要一个顺手的工具。 还重点介绍了Monad,主要是从我等下里巴人的角度解释一下怎么用它。 对于一个完全的新手,其实还应该学习一些关于Haskell基本数据结构和函数定义的知识,不过这方面的东西最好找专门的Haskell语言教程。

这一章,会继续练习Monad的使用。我们还会见到一些来自GHC标准库的强大武器。

这次我们 import 进来一个新的库

import Text.ParserCombinators.Parsec hiding (spaces)

Parsec库是Haskell中专门的解释器工具库。hiding关键字指出,我们导入这个库时,把 spaces 函数排除在外--因为我们随后要自己实现一个不同的逻辑。

Haskell语言: Parsing 代码片段二

symbol :: Parser Char
symbol = oneOf "!#$%&|*+-/:<=>?@^_~"

上面是类型声明,也就是说给oneOf函数传这一串符号进去,它返回一个解释器类型。其接收代码文本,针对oneOf传入的符号串进行处理。类似oneOf的这些解释器Monad生成工具,在Parsec库里还有一些其它的解析工具,后面我们会用到一些。

OK,这个解释器(其实是个解释器零件)我们怎么使用它呢?现在我们写一个解析表达式的工具:

Haskell语言: Parsing 代码片段三

readExpr :: String -&gt; String
readExpr input = case parse symbol "lisp" input of
Left err -&gt; "No match: " ++ show err
Right val -&gt; "Found value"

这个用法很简单,利用 Parsec 库的 parse,我们把定义好的 symbol 传给 parse ("lisp" 作为注册进来的 symbol 的命名),然后接收一个字符串,parse会返回一个 data ,其中 Left 是表示错误信息,err中有具体内容,Right val则是匹配正确后的结果,这里我们先不管它返回了什么,输出一个"Found value"。

这段程序可以看出,symbol 的作用比较简单,只要字符串里有任一个匹配oneOf的字符,解析器会把它做为一个词素提出来。而parse就处理这个解析结果。

这样,很简单的接收命令行参数然后传出解析结果。!!是haskell的列表索引操作符。也就是说,它只处理args的第一个元素。 编译以后执行试试吧,你会看到,它校验你输入的文本中,第一个字符是否在symbol注册的符号中。并返回相应的值。 解释器还很简单,但是后面我们会慢慢完善它。

这一章的重点,在于学会如何组合多个函数或Monad。具体的原理和定义,推荐一份已经被汉化出来的教程:函数式编程另类指南,其中的currying和Continuations知识,与我们在这里使用的组合技术相关。

事实上,如果那东西把你搞糊涂了,倒不如当那些名词和定理不存在,我们继续写程序吧XD。

今天的代码:

Haskell语言: Parsing的完整代码

module Main where

import System.Environment
import Text.ParserCombinators.Parsec hiding (spaces)

symbol :: Parser Char
symbol = oneOf "!#$%&|*+-/:<=>?@^_~"

readExpr :: String -&gt; String
readExpr input = case parse symbol "lisp" input of
Left err -&gt; "No match: " ++ show err
Right val -&gt; "Found value"

main :: IO ()
main = do args &lt;- getArgs
putStrLn (readExpr (args !! 0))

没有评论: