由于有办法通过模式匹配绑定列表的头部和尾部,我想知道你是否可以使用模式匹配来绑定列表的最后一个元素?
答案 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]),假设我们要构建这个列表,其中从末尾开始的原始列表的每个第二个元素是取而代之的是双倍(从Homework1的this 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 。