Haskell 一个有趣的模式

Haskell 一个有趣的模式,haskell,design-patterns,Haskell,Design Patterns,我正在解决99个哈斯克尔问题。我已经成功地解决了问题21,当我打开时,提出了以下解决方案: 在列表中的给定位置插入元素 我发现模式(n+1)很有趣,因为它似乎是一种优雅的方式,可以将insertAt的基于1的参数转换为split的基于0的参数(它的函数来自前面的练习,基本上与splitAt相同)。问题是GHC没有发现这种模式那么优雅,事实上它说: 模式中的分析错误:n+1 我不认为写答案的人是愚蠢的,我想知道这种模式在Haskell中是否合法,如果合法,如何解决这个问题。我相信是的,当《99 H

我正在解决99个哈斯克尔问题。我已经成功地解决了问题21,当我打开时,提出了以下解决方案:

在列表中的给定位置插入元素

我发现模式
(n+1)
很有趣,因为它似乎是一种优雅的方式,可以将
insertAt
的基于1的参数转换为
split
的基于0的参数(它的函数来自前面的练习,基本上与
splitAt
相同)。问题是GHC没有发现这种模式那么优雅,事实上它说:

模式中的分析错误:n+1

我不认为写答案的人是愚蠢的,我想知道这种模式在Haskell中是否合法,如果合法,如何解决这个问题。

我相信是的,当《99 Haskell问题》的作者写这个解决方案时,很可能也是如此,但它不再在Haskell中。

请注意,您现在可以

例如:

{-# 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

n+k
模式的问题可以追溯到Haskell中的一个设计决策,即通过名称的第一个字符来区分模式中的构造函数和变量。如果回到ML,一个公共函数定义可能如下所示(使用Haskell语法)

如您所见,在第一行的LHS上,从语法上讲,
f
nil
之间没有区别,但它们有不同的角色
f
是需要绑定到
map
的第一个参数的变量,
nil
是需要与第二个参数匹配的构造函数。现在,ML通过在周围的范围中查找每个变量,并在查找失败时假设名称是变量,从而实现了这一区别。因此,当查找失败时,
nil
被识别为构造函数。但是考虑一下当模式中有一个键入时会发生什么:

map f niil = nil
(在
niil
中有两个
i
s)
niil
不是作用域中的构造函数名称,因此它被视为变量,并且定义被错误地解释

Haskell对此问题的解决方案是要求构造函数名以大写字母开头,变量名以小写字母开头。而且,对于中缀运算符/构造函数,构造函数名称必须以
开头,而运算符名称不能以
开头。这也有助于区分解构绑定:

x:xn = ...
显然是解构绑定,因为在

n - m = ...
显然是一个函数定义,因为
-
不能是构造函数名

但是允许使用
n+k
模式,比如
n+1
,意味着
+
既是一个有效的函数名,也是一个类似于模式中构造函数的东西。现在

n + 1 = ...
又是模棱两可,;它可以是名为
(+)
的函数定义的一部分,也可以是
n
的解构模式匹配定义。在Haskell 98中,通过声明

n + 1 = ...
函数定义,以及

(n + 1) = ...

解构绑定。但这显然不是一个令人满意的解决方案。

尽管我认为
n+k
-模式确实有一定的吸引力,但在基于1的表示和基于零的表示之间进行转换将永远不是一个合适的用例。它们是用来递归地“解构”数字的FWIW,
insertAt
无论如何应该是基于零的,不是吗?@leftaroundabout,
insertAt
应该是基于1的,根据给定的示例:
insertAt'X'”abcd“2
->
“aXbcd”
。是的,我的意思是,它确实应该被指定为基于0的,就像所有Haskell标准列表函数一样()。
n + 1 = ...
(n + 1) = ...