一个有趣的模式

时间:2014-09-11 13:02:36

标签: haskell design-patterns

我正在解决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中是否合法,如果是,如何修复解决方案。

3 个答案:

答案 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上fnil之间没有区别,但它们具有不同的作用; 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