Mar*_*ark 6 haskell design-patterns
我正在解决99个Haskell Probems.我已成功解决了第21号问题,当我打开解决方案页面时,提出了以下解决方案:
将给定位置的元素插入列表.
insertAt :: a -> [a] -> Int -> [a]
insertAt x xs (n+1) = let (ys,zs) = split xs n in ys++x:zs
Run Code Online (Sandbox Code Playgroud)
我发现模式(n + 1)
很有趣,因为它似乎是一种优雅的方式将基于1的参数转换为基于insertAt
0的参数split
(它的功能来自之前的练习,基本上相同splitAt
).问题是GHC没有发现这种优雅的模式,事实上它说:
模式中的解析错误:n + 1
我不认为写这个答案的人是愚蠢的,我想知道这种模式在Haskell中是否合法,如果是,那么如何修复解决方案.
n+k
模式的问题可以追溯到Haskell中的设计决策,以通过名称的第一个字符区分构造函数和模式中的变量.如果你回到ML,一个常见的函数定义可能看起来像(使用Haskell语法)
map f nil = nil
map f (x:xn) = f x : map f xn
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,从语法上讲,第一行的LHS 之间f
和之间没有区别nil
,但它们具有不同的作用; f
是一个需要绑定到第一个参数的变量,map
而while nil
是一个需要与第二个参数匹配的构造函数.现在,ML通过在周围范围内查找每个变量来进行区分,并假设查找失败时名称是变量.因此nil
在查找失败时被识别为构造函数.但考虑一下在模式中出现错字时会发生什么:
map f niil = nil
Run Code Online (Sandbox Code Playgroud)
(2个i
S IN niil
). niil
不是作用域中的构造函数名称,因此它被视为变量,并且静默地解释定义.
Haskell解决这个问题的方法是要求构造函数名称以大写字母开头,变量名称以小写字母开头.并且,对于中缀运算符/构造函数,构造函数名称必须以:
操作符名称可能不以的开头:
.这也有助于区分解构绑定:
x:xn = ...
Run Code Online (Sandbox Code Playgroud)
显然是一个解构绑定,因为你不能定义命名函数:
,而
n - m = ...
Run Code Online (Sandbox Code Playgroud)
显然是一个函数定义,因为-
它不能是构造函数名.
但是允许n+k
模式,比如n+1
,意味着+
它既是一个有效的函数名,又是一个像模式中的构造函数一样的东西.现在
n + 1 = ...
Run Code Online (Sandbox Code Playgroud)
再次模棱两可; 它可以是命名函数定义的一部分(+)
,也可以是解构模式匹配定义n
.在Haskell 98中,这种模糊性通过声明来解决
n + 1 = ...
Run Code Online (Sandbox Code Playgroud)
一个函数定义,和
(n + 1) = ...
Run Code Online (Sandbox Code Playgroud)
解构约束力.但这显然不是一个令人满意的解决方案.
归档时间: |
|
查看次数: |
163 次 |
最近记录: |