Haskell函数类型与定义不兼容:escape函数相关疑问
Haskell escape 函数相关疑问解答
参考的原函数实现如下:
escape :: String -> String escape = let escapeChar c = case c of '<' -> "<" '>' -> ">" _ -> [c] in concat . map escapeChar
为什么escape定义里没有显式声明入参?
这是Haskell里很常用的无点写法(point-free style),核心逻辑靠两个特性支撑:
- 首先Haskell里函数是一等公民,你可以直接把一个函数表达式赋值给同名绑定,不需要额外包裹
- 其次
.是函数组合运算符,规则是(f . g) x = f (g x):组合两个函数得到的新函数,会自动接收一个参数,先传给右边的g处理,结果再传给f处理
你看到的concat . map escapeChar本身就已经是一个String -> String类型的函数了:它接收的字符串会先被map escapeChar处理(把每个字符转义,得到[String]类型的结果),再传给concat把所有转义后的片段拼成完整字符串。这个写法和下面显式写参数的版本100%等价:
escape inputStr = concat (map escapeChar inputStr)
无点写法只是省掉了两边重复出现的参数名,和数学里写f = g ∘ h不用特意写f(x) = g(h(x))是一个思路。
escapeChar和后面的c是什么关系?
c就是escapeChar的形参,没有任何特殊隐含规则。
Haskell定义函数的语法本身就是函数名 形参1 形参2 ... = 函数体,跟在函数名后面的标识符就是对应位置的形参:当你调用函数传入实参时,实参会按位置绑定到对应的形参名上,供函数体内部使用。
具体到这个例子:
- 调用
escapeChar '<'时,形参c就绑定到字符'<',case匹配到对应分支返回"<" - 调用
escapeChar 'a'时,c绑定到'a',匹配通配分支返回[c]也就是单字符字符串"a"(Haskell中String本质是字符列表[Char],单元素列表就是长度为1的字符串)
如果觉得这个写法不好理解,你可以把它改写成lambda形式,两者行为完全一致:
escapeChar = \c -> case c of '<' -> "<" '>' -> ">" _ -> [c]
这里就能很直观看到,c就是函数接收的输入参数的名字而已。
内容的提问来源于stack exchange,提问作者ghg534
相关产品推荐
相关产品推荐

