简短的回答是:模式匹配无法做到这一点,您必须使用函数。
长答案是:它不在标准 Haskell 中,但如果您愿意使用名为 View Patterns 的扩展,并且如果您的模式匹配最终花费比恒定时间更长的时间没有问题。
原因是模式匹配首先基于结构的构造方式。列表是一种抽象类型,其结构如下:
data List a = Empty | Cons a (List a)
deriving (Show) -- this is just so you can print the List
当您声明这样的类型时,您会生成三个对象:一个类型构造函数List,以及两个数据构造函数:Empty 和Cons。类型构造函数接受类型并将它们转换为其他类型,即List 接受类型a 并创建另一个类型List a。数据构造函数的工作方式类似于返回 List a 类型的函数。在这种情况下,您有:
Empty :: List a
代表一个空列表和
Cons :: a -> List a -> List a
它接受a 类型的值和一个列表,并将该值附加到列表的头部,返回另一个列表。所以你可以像这样构建你的列表:
empty = Empty -- similar to []
list1 = Cons 1 Empty -- similar to 1:[] = [1]
list2 = Cons 2 list1 -- similar to 2:(1:[]) = 2:[1] = [2,1]
这或多或少是列表的工作方式,但是在 Empty 的位置上你有 [] 在Cons 的位置上你有 (:)。当您键入 [1,2,3] 之类的内容时,这只是 1:2:3:[] 或 Cons 1 (Cons 2 (Cons 3 Empty)) 的语法糖。
当您进行模式匹配时,您正在“解构”该类型。了解类型的结构可以让您以独特的方式反汇编它。考虑函数:
head :: List a -> a
head (Empty) = error " the empty list have no head"
head (Cons x xs) = x
类型匹配发生的情况是数据构造函数与您提供的某些结构相匹配。如果它匹配Empty,那么你有一个空列表。如果 if 匹配 Const x xs 则 x 必须具有类型 a 并且必须是列表的头部,xs 必须具有类型 List a 并且是列表的尾部,因为这是数据构造函数的类型:
Cons :: a -> List a -> List a
如果Cons x xs 是List a 类型,则x 必须是a 并且xs 必须是List a。 (x:xs) 也是如此。如果您查看 GHCi 中 (:) 的类型:
> :t (:)
(:) :: a -> [a] -> [a]
因此,如果 (x:xs) 的类型为 [a],则 x 必须是 a 并且 xs 必须是 [a] 。当您尝试执行(xs:x) 然后将xs 视为列表时收到的错误消息正是因为这个原因。通过使用(:),编译器推断xs 具有a 类型,并且通过使用
++,它推断xs必须是[a]。然后它吓坏了,因为没有有限类型a 用于哪个a = [a] - 这就是他试图通过该错误消息告诉你的内容。
如果您需要以与数据构造器构建结构的方式不匹配的其他方式分解结构,则必须编写自己的函数。标准库中有两个函数可以满足您的需求:last 返回列表的最后一个元素,init 返回列表的最后一个元素。
但请注意,模式匹配发生在恒定时间内。要找出列表的头部和尾部,无论列表有多长,您只需要查看最外层的数据构造函数即可。找到最后一个元素是O(N):您必须挖掘直到找到最里面的Cons 或最里面的(:),这需要您“剥离”结构N 次,其中N 是列表的大小。
如果您经常需要在长列表中查找最后一个元素,您可能会考虑使用列表是否是一个好主意。你可以去Data.Sequence(恒定时间访问第一个和最后一个元素),Data.Map(log(N) 时间访问任何元素,如果你知道它的键),Data.Array(恒定时间访问一个元素,如果你知道它的索引)、Data.Vector 或其他比列表更符合您需求的数据结构。
好的。那是简短的答案(:P)。很长的,您需要自己查找一下,但这里有一个介绍。
通过使用视图模式,您可以使用非常接近模式匹配的语法来实现这一点。 View Patterns 是一个扩展,您可以通过将其作为代码的第一行来使用它:
{-# Language ViewPatterns #-}
使用说明在这里:http://hackage.haskell.org/trac/ghc/wiki/ViewPatterns
使用视图模式,您可以执行以下操作:
view :: [a] -> (a, [a])
view xs = (last xs, init xs)
someFunction :: [a] -> ...
someFunction (view -> (x,xs)) = ...
x 和 xs 将绑定到您提供给 someFunction 的列表的 last 和 init。从语法上讲,它感觉就像模式匹配,但实际上只是将last 和init 应用于给定列表。