我正在解决99个Haskell Probems。我已成功解决了问题21,当我打开solution page时,提出了以下解决方案:
将给定位置的元素插入列表。
insertAt :: a -> [a] -> Int -> [a]
insertAt x xs (n+1) = let (ys,zs) = split xs n in ys++x:zs
我发现模式(n + 1)
很有趣,因为它似乎是一种优雅的方法,可以将基于1的insertAt
参数转换为基于0的split
参数(它的功能来自之前的练习,与splitAt
基本相同。问题是GHC没有发现这种优雅的模式,事实上它说:
模式中的解析错误:n + 1
我不认为写这个答案的人是愚蠢的,我想知道这种模式在Haskell中是否合法,如果是,如何修复解决方案。
答案 0 :(得分:12)
我相信它已经removed from the language,因此很可能在99 Haskell Problems的作者编写该解决方案时,但它已经不在Haskell中了。
答案 1 :(得分:6)
n+k
模式的问题可以追溯到Haskell中的设计决策,以通过名称的第一个字符来区分构造函数和模式中的变量。如果你回到ML,常见的函数定义可能看起来像(使用Haskell语法)
map f nil = nil
map f (x:xn) = f x : map f xn
正如您所看到的,从语法上讲,第一行的LHS上f
和nil
之间没有区别,但它们具有不同的作用; f
是需要绑定到map
的第一个参数的变量,而nil
是需要与第二个匹配的构造函数。现在,ML通过在周围范围内查找每个变量来进行区分,并假设查找失败时名称是变量。因此,nil
在查找失败时被识别为构造函数。但是考虑当模式中存在拼写错误时会发生什么:
map f niil = nil
(i
中的两个niil
)。 niil
不是作用域中的构造函数名称,因此它被视为变量,并且该定义被无提示地解释错误。
Haskell对此问题的解决方案是要求构造函数名称以大写字母开头,变量名称以小写字母开头。并且,对于中缀运算符/构造函数,构造函数名称必须以:
开头,而运算符名称不能以:
开头。这也有助于区分解构绑定:
x:xn = ...
显然是一种解构绑定,因为你无法定义一个名为:
的函数,而
n - m = ...
显然是一个函数定义,因为-
不能是构造函数名称。
但允许n+k
模式(如n+1
)意味着+
既是有效的函数名,又是模式中构造函数的作用。现在
n + 1 = ...
再次含糊不清;它可以是名为(+)
的函数定义的一部分,也可以是n
的解构模式匹配定义。在Haskell 98中,通过声明
n + 1 = ...
函数定义,
(n + 1) = ...
解构约束。但这显然不是一个令人满意的解决方案。
答案 2 :(得分:1)
请注意,您现在可以use view patterns instead of n+1。
例如:
{-# LANGUAGE ViewPatterns #-}
module Temp where
import Data.List (splitAt)
split :: [a] -> Int -> ([a], [a])
split = flip splitAt
insertAt :: a -> [a] -> Int -> [a]
insertAt x xs (subtract 1 -> n) = let (ys,zs) = split xs n in ys++x:zs