使用模式查找第n个元素

abc*_*bcd 6 haskell

我正在通过Learn You A Haskell工作,以便快速掌握Haskell的基础知识.我对函数式编程和模式匹配都很满意,但后者更像是Mathematica如何做到这一点.

本着head第4.1章中幼稚实施的精神,我进行了一个天真的实现last:

last1 :: [a] -> a
last1 (_:x:[]) = x
Run Code Online (Sandbox Code Playgroud)

但是,调用last1 [1,2,3,4]给出了错误Exception: ... Non-exhaustive patterns in function last1.我理解这个错误意味着指定的模式并不涵盖所有可能的输入,通常,需要一个包罗万象的模式(我没有提供).但是,我不确定为什么我的输入会出现错误.

问题1:我(我的错误方法)的理解是第一个元素被捕获,_其余元素被分配x,这不是我想要的.但是,这不应该给出类型错误,因为我指定了[a] -> a,但x现在是一个列表?

请注意,这不是关于如何编写工作last函数 - 我知道我可以将其编写为(以及其他可能性)

last2 :: [a] -> a
last2 [x] = x
last2 (_:x) = last2 x
Run Code Online (Sandbox Code Playgroud)

问题2:在更好地理解Haskell中的模式匹配的同一主题中,我如何使用模式匹配来挑选最后一个元素,或者更一般地,n从给定列表中选择th元素,比方说,[1..10]

这个答案表明你可以使用与ViewPatterns扩展名的模式匹配来绑定最后一个元素,但似乎很奇怪没有类似的"简单"模式.head

Mathematica中,我可能会把它写成:

Range[10] /. {Repeated[_, {5}], x_, ___} :> x
(* 6 *)
Run Code Online (Sandbox Code Playgroud)

选出第6个元素和

Range[10] /. {___, x_} :> x
(* 10 *)
Run Code Online (Sandbox Code Playgroud)

挑出非空列表的最后一个元素.

如果我在文中稍后讨论这个问题,我会道歉,但是我试图将每个主题和概念与我遇到的问题联系起来,以及如何处理我知道的其他语言,以便我能够理解差异和相似之处.

sab*_*uma 8

要了解第一次尝试的结果,您需要查看列表数据的定义方式.列表享有一些特殊的语法,但你会写这样的东西.

data List a = (:) a (List a)
            | []
Run Code Online (Sandbox Code Playgroud)

所以,你的列表[1 .. 10]实际上是按结构的

(1 : (2 : (3 : (4 : []))))
Run Code Online (Sandbox Code Playgroud)

另外,由于(:)运算符的正确关联性,last1的模式实际上看起来像

last1 :: [a] -> a
last1 (_:(x:[])) = x
Run Code Online (Sandbox Code Playgroud)

这就是为什么'x'与列表中的元素具有相同的类型; 它是(:)构造函数的第一个参数.

模式匹配允许您解构数据结构,如列表,但您需要知道他们必须做什么"形状".这就是为什么你不能直接指定一个模式来提取列表的最后一个元素,因为列表可以有无限长度.这就是工作解决方案(last2)使用递归来解决问题的原因.你知道一个长度列表的模式是什么,以及在哪里找到最终元素; 对于其他所有内容,您可以丢弃第一个元素并提取生成的较短列表的最后一个元素.

如果你愿意,你可以添加更多的模式,但它不会证明是有帮助的.你可以把它写成

last2 :: [a] -> a
last2 (x:[])     = x
last2 (_:x:[])   = x
last2 (_:_:x:[]) = x
        ...
last2 (x:xs) = last2 xs
Run Code Online (Sandbox Code Playgroud)

但是,如果没有无数个案例,您永远无法为所有长度的输入列表完成该功能.当你考虑到列表实际上可以无限长的事实时,它更加可疑; 你会用什么模式来匹配呢?

  • 这里隐含的是_Haskell只允许在constructors_上进行模式匹配.模式匹配的语义实际上非常"简单" - 参见[Haskell 2010报告](http://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-580003.17).Haskell 98有一个名为_n + k patterns_的功能,它基本上是你想要的,但应用于数字,而不是列表,但它已被删除. (3认同)