【发布时间】:2011-09-27 23:42:13
【问题描述】:
既然有办法通过模式匹配来绑定列表的头部和尾部,我想知道你是否可以使用模式匹配来绑定列表的最后一个元素?
【问题讨论】:
标签: haskell
既然有办法通过模式匹配来绑定列表的头部和尾部,我想知道你是否可以使用模式匹配来绑定列表的最后一个元素?
【问题讨论】:
标签: haskell
这是我使用 Haskell 编程的第一天,我也遇到了同样的问题,但我无法解决使用以前解决方案中建议的某种外部工件。
我对 Haskell 的感觉是,如果核心语言无法解决您的问题,那么解决方案就是转换您的问题,直到它适用于该语言。
在这种情况下,转换问题意味着将尾部问题转换为头部问题,这似乎是模式匹配中唯一支持的操作。事实证明,您可以使用列表反转轻松地做到这一点,然后使用头元素处理反向列表,就像在原始列表中使用尾元素一样,最后,如有必要,将结果恢复为初始顺序(例如,如果这是一个列表)。
例如,给定一个整数列表(例如 [1,2,3,4,5,6]),假设我们要构建这个列表,其中从末尾开始的原始列表的每个第二个元素是替换为双精度(练习取自 this excellent introduction to Haskell 的 Homework1):[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。
【讨论】:
last 可能会更糟......如果性能至关重要并且您需要访问最后一个元素,那么列表不是合适的类型)。
是的,您可以使用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) 访问。
【讨论】:
Maybe 的safeLast 函数,然后您可以对其进行模式匹配,或者只是反转列表然后使用普通模式匹配.
f = (*2) . last 更好。
其他答案解释了基于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"
【讨论】:
您可以使用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 是您喜欢的任何功能。
(扩展的目的之一是让人们干净、轻松地使用访问器功能
用于模式匹配,因此他们不必使用其数据类型的底层结构
在上面定义函数。)
【讨论】: