Haskell: 模式 x++xs 中的解析错误
Haskell: Parse error in pattern x ++ xs
做 third of the 99-Haskell problems(我目前正在尝试学习这门语言)我尝试将模式匹配和递归合并到我的函数中,现在看起来像这样:
myElementAt :: [a] -> Int -> a
myElementAt (x ++ xs) i =
if length (x ++ xs) == i && length xs == 1 then xs!!0
else myElementAt x i
这给了我 Parse error in pattern: x ++ xs
。问题:
- 为什么这会给我一个解析错误?是因为 Haskell 不知道在哪里削减我的名单(这是我最好的猜测)?
- 如何重构我的函数以使其正常工作?算法思想是检查list是否有指定的inde长度;如果是 return 最后一个元素;如果没有在列表末尾切掉一个元素,然后进行递归。
注意:我知道这是一个非常糟糕的算法,但我给自己设置了一个挑战来编写该函数,包括递归和模式匹配。我也尝试不使用 !!
运算符,但这对我来说很好,因为它真正做的唯一一件事(或者如果编译应该做的)就是将单元素列表转换为该元素。
据推测,您正在尝试匹配列表的头部和尾部。让我们逐步了解它:
myElementAt (x:_) 0 = x
意思是如果head是x
,tail是东西,index是0,return就是head。请注意,您的 x ++ x
是两个列表的串联,而不是头部和尾部。
那你可以
myElementAt(_:tl) i = myElementAt tl (i - 1)
表示如果前面的模式没有匹配到,则忽略头部,取尾部的i - 1个元素。
Haskell 有两种不同类型的值级实体:变量(这也包括函数、中缀运算符,如 ++
等)和 构造函数。两者都可以用在表达式中,但只有构造函数也可以用在模式中。
在任何一种情况下,很容易判断您是在处理变量还是构造函数:构造函数始终以大写字母开头(例如 Nothing
, True
or StateT
) or, if it's an infix, with a colon (:
, :+
)。其他一切都是变量。从根本上说,区别在于构造函数始终是来自预定义集合(即 data
定义的替代项)的唯一且可立即匹配的值,而变量可以具有 任何值,并且通常原则上不可能唯一区分不同的变量,特别是如果它们具有函数类型。
你的实际上是一个很好的例子:为了使模式匹配 x ++ xs
有意义,必须有 一种独特的方式 输入列表可以写成x ++ xs
的形式。好吧,但是对于 [0,1,2,3]
,有多种不同的方法可以做到这一点:
[] ++[0,1,2,3]
[0] ++ [1,2,3]
[0,1] ++ [2,3]
[0,1,2] ++ [3]
[0,1,2,3]++ []
runtime应该选择哪一个?
在模式中,您只能使用 :
和 []
等构造函数。附加运算符 (++)
是一个非构造函数。
所以,试试这样的东西:
myElementAt :: [a] -> Int -> a
myElementAt (x:xs) i = ...
您的代码中还有更多问题,但至少这解决了您的第一个问题。
在标准 Haskell 模式中匹配如下:
f :: Int -> Int
f (g n 1) = n
g :: Int -> Int -> Int
g a b = a+b
是非法的,因为模式中不允许调用函数,您的情况只是一个特例,因为运算符 ++
只是一个函数。
要对列表进行模式匹配,您可以这样做:
myElementAt :: [a] -> Int -> a
myElementAt (x:xs) i = // result
但在这种情况下 x
是类型 a
而不是 [a]
,它是列表的头部而 xs
是它的尾部,你需要要更改您的函数实现以适应这一事实,此函数也会因空列表 []
而失败。然而,这是模式匹配 aginst 列表的惯用 haskell 方式。
我应该提一下,当我说 "illegal" 时,我的意思是在标准 Haskell 中,有 GHC 扩展提供了类似的东西,它被称为 ViewPatterns
但我没有认为你需要它,尤其是你还在学习。
做 third of the 99-Haskell problems(我目前正在尝试学习这门语言)我尝试将模式匹配和递归合并到我的函数中,现在看起来像这样:
myElementAt :: [a] -> Int -> a
myElementAt (x ++ xs) i =
if length (x ++ xs) == i && length xs == 1 then xs!!0
else myElementAt x i
这给了我 Parse error in pattern: x ++ xs
。问题:
- 为什么这会给我一个解析错误?是因为 Haskell 不知道在哪里削减我的名单(这是我最好的猜测)?
- 如何重构我的函数以使其正常工作?算法思想是检查list是否有指定的inde长度;如果是 return 最后一个元素;如果没有在列表末尾切掉一个元素,然后进行递归。
注意:我知道这是一个非常糟糕的算法,但我给自己设置了一个挑战来编写该函数,包括递归和模式匹配。我也尝试不使用 !!
运算符,但这对我来说很好,因为它真正做的唯一一件事(或者如果编译应该做的)就是将单元素列表转换为该元素。
据推测,您正在尝试匹配列表的头部和尾部。让我们逐步了解它:
myElementAt (x:_) 0 = x
意思是如果head是x
,tail是东西,index是0,return就是head。请注意,您的 x ++ x
是两个列表的串联,而不是头部和尾部。
那你可以
myElementAt(_:tl) i = myElementAt tl (i - 1)
表示如果前面的模式没有匹配到,则忽略头部,取尾部的i - 1个元素。
Haskell 有两种不同类型的值级实体:变量(这也包括函数、中缀运算符,如 ++
等)和 构造函数。两者都可以用在表达式中,但只有构造函数也可以用在模式中。
在任何一种情况下,很容易判断您是在处理变量还是构造函数:构造函数始终以大写字母开头(例如 Nothing
, True
or StateT
) or, if it's an infix, with a colon (:
, :+
)。其他一切都是变量。从根本上说,区别在于构造函数始终是来自预定义集合(即 data
定义的替代项)的唯一且可立即匹配的值,而变量可以具有 任何值,并且通常原则上不可能唯一区分不同的变量,特别是如果它们具有函数类型。
你的实际上是一个很好的例子:为了使模式匹配 x ++ xs
有意义,必须有 一种独特的方式 输入列表可以写成x ++ xs
的形式。好吧,但是对于 [0,1,2,3]
,有多种不同的方法可以做到这一点:
[] ++[0,1,2,3]
[0] ++ [1,2,3]
[0,1] ++ [2,3]
[0,1,2] ++ [3]
[0,1,2,3]++ []
runtime应该选择哪一个?
在模式中,您只能使用 :
和 []
等构造函数。附加运算符 (++)
是一个非构造函数。
所以,试试这样的东西:
myElementAt :: [a] -> Int -> a
myElementAt (x:xs) i = ...
您的代码中还有更多问题,但至少这解决了您的第一个问题。
在标准 Haskell 模式中匹配如下:
f :: Int -> Int
f (g n 1) = n
g :: Int -> Int -> Int
g a b = a+b
是非法的,因为模式中不允许调用函数,您的情况只是一个特例,因为运算符 ++
只是一个函数。
要对列表进行模式匹配,您可以这样做:
myElementAt :: [a] -> Int -> a
myElementAt (x:xs) i = // result
但在这种情况下 x
是类型 a
而不是 [a]
,它是列表的头部而 xs
是它的尾部,你需要要更改您的函数实现以适应这一事实,此函数也会因空列表 []
而失败。然而,这是模式匹配 aginst 列表的惯用 haskell 方式。
我应该提一下,当我说 "illegal" 时,我的意思是在标准 Haskell 中,有 GHC 扩展提供了类似的东西,它被称为 ViewPatterns
但我没有认为你需要它,尤其是你还在学习。