你可以使用模式匹配来绑定列表的最后一个元素吗?

时间:2011-09-27 23:42:13

标签: haskell

由于有办法通过模式匹配绑定列表的头部和尾部,我想知道你是否可以使用模式匹配来绑定列表的最后一个元素?

4 个答案:

答案 0 :(得分:17)

是的,您可以使用ViewPatterns扩展程序。

Prelude> :set -XViewPatterns
Prelude> let f (last -> x) = x*2
Prelude> f [1, 2, 3]
6

请注意,此模式将始终成功,因此您可能希望为列表为空的情况添加模式,否则last将引发异常。

Prelude> f []
*** Exception: Prelude.last: empty list

另请注意,这只是语法糖。与普通模式匹配不同,这是 O(n),因为您仍在访问单链表的最后一个元素。如果您需要更高效的访问权限,请考虑使用其他数据结构,例如Data.Sequence,它提供对两端的 O(1)访问权限。

答案 1 :(得分:11)

您可以使用ViewPatterns在列表末尾进行模式匹配,让我们来做

{-# LANGUAGE ViewPatterns #-}

并使用reverse作为viewFunction,因为它总是成功,例如

printLast :: Show a => IO ()
printLast (reverse -> (x:_)) = print x
printLast _ = putStrLn "Sorry, there wasn't a last element to print."

这是安全的,只要你涵盖所有可能性,它就不会抛出任何异常。 (例如,您可以重写它以返回Maybe。)

语法

mainFunction (viewFunction -> pattern) = resultExpression

的语法糖

mainFunction x = case viewFunction x of pattern -> resultExpression

所以你可以看到它实际上只是反转列表然后模式匹配,但它感觉更好。 viewFunction只是你喜欢的任何功能。 (扩展的目的之一是允许人们干净利落地使用访问者功能 对于模式匹配,所以他们不必使用其数据类型的底层结构 在其上定义函数。)

答案 2 :(得分:4)

其他答案解释了基于ViewPatterns的解决方案。如果你想让它更像模式匹配,你可以把它打包成PatternSynonym

tailLast :: [a] -> Maybe ([a], a)
tailLast xs@(_:_) = Just (init xs, last xs)
tailLast _ = Nothing

pattern Split x1 xs xn = x1 : (tailLast -> Just (xs, xn))

然后将您的功能写为例如。

foo :: [a] -> (a, [a], a)
foo (Split head mid last) = (head, mid, last)
foo _ = error "foo: empty list"

答案 3 :(得分:2)

这是我的Haskell编程的第一天,我也遇到了同样的问题,但我无法解决使用某种外部工件,如前面的解决方案所示。

我对Haskell的感觉是,如果核心语言没有针对您的问题的解决方案,那么解决方案就是将您的问题转换为适用于该语言的问题。

在这种情况下,转换问题意味着将尾部问题转换为头部问题,这似乎是模式匹配中唯一受支持的操作。转向您可以使用列表反转轻松地执行此操作,然后使用head元素处理反向列表,就像在原始列表中使用tail元素一样,最后,如果需要,将结果恢复为初始顺序(例如,如果这是一个清单。)

例如,给定一个整数列表(例如[1,2,3,4,5,6]),假设我们要构建这个列表,其中从末尾开始的原始列表的每个第二个元素是取而代之的是双倍(从Homework1this excellent introduction to Haskell取得的练习):[2,2,6,4,10,6]。

然后我们可以使用以下内容:

revert :: [Integer] -> [Integer]
revert []     = []
revert (x:[]) = [x]
revert (x:xs) = (revert xs) ++ [x]

doubleSecond :: [Integer] -> [Integer]
doubleSecond []       = []
doubleSecond (x:[])   = [x]
doubleSecond (x:y:xs) = (x:2*y : (doubleSecond xs))

doubleBeforeLast :: [Integer] -> [Integer]
doubleBeforeLast l = ( revert (doubleSecond (revert l)) )

main = putStrLn (show (doubleBeforeLast [1,2,3,4,5,6,7,8,9]))

它显然比以前的解决方案长得多,但它对我来说感觉更多 Haskell-ish