我正在通过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)
挑出非空列表的最后一个元素.
如果我在文中稍后讨论这个问题,我会道歉,但是我试图将每个主题和概念与我遇到的问题联系起来,以及如何处理我知道的其他语言,以便我能够理解差异和相似之处.
要了解第一次尝试的结果,您需要查看列表数据的定义方式.列表享有一些特殊的语法,但你会写这样的东西.
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)
但是,如果没有无数个案例,您永远无法为所有长度的输入列表完成该功能.当你考虑到列表实际上可以无限长的事实时,它更加可疑; 你会用什么模式来匹配呢?