我正在观看有关haskell https://www.youtube.com/watch?v=9FGThag0Fqs中解析器的教程。讲座从定义一些非常基本的解析器开始。这些将在以后一起用于创建更复杂的解析器。其中一个基本解析器是 item 。这用于从我们正在解析的字符串中提取字符。
所有解析器都具有以下类型:
type Parser a = String -> [(a, String)]
解析器项的定义如下:
item :: Parser Char
item = \inp -> case inp of
[] -> []
(x:xs) -> [(x,xs)]
我不习惯这种语法,所以它看起来很奇怪。我会写的:
item' :: Parser Char
item' [] = []
item' (x:xs) = [(x,xs)]
在ghci中测试它表明它们是相同的:
*Main> item ""
[]
*Main> item "abc"
[('a',"bc")]
*Main> item' ""
[]
*Main> item' "abc"
[('a',"bc")]
讲师简短地评论它看起来更清晰,但我不同意。所以我的问题是:
他们确实完全相同吗? 为什么lambda版本更清晰?
答案 0 :(得分:11)
我相信这来自于写作的常见做法
f :: Type1 -> ... -> Typen -> Result
f x1 ... xn = someResult
我们在类型中有完全n
个函数箭头,并且在等式左边有正好n
个参数。这样可以很容易地将类型和形式参数联系起来。
如果Result
是函数的类型别名,那么我们可以写
f :: Type1 -> ... -> Typen -> Result
f x1 ... xn y = something
或
f :: Type1 -> ... -> Typen -> Result
f x1 ... xn = \y -> something
后者遵循上述惯例:n
箭头,左侧是n
个变量。此外,在右侧,我们有Result
类型的东西,使其更容易被发现。前者却没有,在快速阅读代码时可能会错过额外的参数。
此外,此样式可以轻松地将Result
转换为新类型而不是类型别名:
newtype Result = R (... -> ...)
f :: Type1 -> ... -> Typen -> Result
f x1 ... xn = R $ \y -> something
发布的item :: Parser Char
代码是n=0
时的此样式的实例。
答案 1 :(得分:4)
为什么你应该避免使用等式函数定义(作者:Roman Cheplyaka): http://ro-che.info/articles/2014-05-09-clauses
以上链接的主要观点:
这并不能解释lambda ..
答案 2 :(得分:3)
我认为他们绝对平等。 lambda样式定义将名称item
放入匿名lambda函数中,该函数在内部进行模式匹配。模式匹配样式定义直接定义它。但最终两者都是进行模式匹配的函数。我认为这是个人品味的问题。
此外,lambda样式的定义可以被认为是pointfree样式,即一个函数定义而没有明确写下它的参数实际上它不是很自由,因为参数仍然是写的(但在不同的地方),但在这种情况下你不能得到任何东西。
这是另一种可能的定义,介于两者之间:
item :: Parser Char
item inp = case inp of
[] -> []
(x:xs) -> [(x, xs)]
它基本上与lambda风格完全相同,但不是免费的。