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

发布于 2024-12-06 14:18:50 字数 59 浏览 0 评论 0原文

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

Since there is a way to bind the head and tail of a list via pattern matching, I'm wondering if you can use pattern matching to bind the last element of a list?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

反目相谮 2024-12-13 14:18:50

是的,您可以使用 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),因为您仍在访问单链表的最后一个元素。如果您需要更有效的访问,请考虑使用不同的数据结构,例如 < code>Data.Sequence,它提供对两端的 O(1) 访问权限。

Yes, you can, using the ViewPatterns extension.

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

Note that this pattern will always succeed, though, so you'll probably want to add a pattern for the case where the list is empty, else last will throw an exception.

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

Also note that this is just syntactic sugar. Unlike normal pattern matching, this is O(n), since you're still accessing the last element of a singly-linked list. If you need more efficient access, consider using a different data structure such as Data.Sequence, which offers O(1) access to both ends.

枕梦 2024-12-13 14:18:50

您可以使用 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 的模式 -> resultExpression

所以你可以看到它实际上只是反转列表然后模式匹配它,但感觉更好。
viewFunction 是您喜欢的任何函数。
(扩展的目的之一是让人们能够干净、轻松地使用访问器函数
用于模式匹配,因此他们不必在以下情况下使用其数据类型的底层结构:
在其上定义函数。)

You can use ViewPatterns to do pattern matching at the end of a list, so let's do

{-# LANGUAGE ViewPatterns #-}

and use reverse as the viewFunction, because it always succeeds, so for example

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

This is safe in the sense that it doesn't throw any exceptions as long as you covered all the possibilities.
(You could rewrite it to return a Maybe, for example.)

The syntax

mainFunction (viewFunction -> pattern) = resultExpression

is syntactic sugar for

mainFunction x = case viewFunction x of pattern -> resultExpression

so you can see it actually just reverses the list then pattern matches that, but it feels nicer.
viewFunction is just any function you like.
(One of the aims of the extension was to allow people to cleanly and easily use accessor functions
for pattern matching so they didn't have to use the underlying structure of their data type when
defining functions on it.)

软的没边 2024-12-13 14:18:50

其他答案解释了基于 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"

The other answers explain the ViewPatterns-based solutions. If you want to make it more pattern matching-like, you can package that into a 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))

and then write your function as e.g.

foo :: [a] -> (a, [a], a)
foo (Split head mid last) = (head, mid, last)
foo _ = error "foo: empty list"
对你再特殊 2024-12-13 14:18:50

这是我学习 Haskell 编程的第一天,我也遇到了同样的问题,但我无法按照之前的解决方案中的建议使用某种外部工件。

我对 Haskell 的感觉是,如果核心语言无法解决你的问题,那么解决方案就是改变你的问题,直到它适用于该语言。

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

例如,给定一个整数列表(例如[1,2,3,4,5,6]),假设我们要构建这个列表,其中原始列表中从末尾开始的每个第二个元素都被替换为它的双(练习取自家庭作业1 href="http://www.cis.upenn.edu/~cis194/spring13/lectures.html" rel="nofollow noreferrer">这篇关于 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 风格。

This is my first day of Haskell programming and I also encountered the same issue, but I could not resolve to use some kind of external artifact as suggested in previous solutions.

My feeling about Haskell is that if the core language has no solution for your problem, then the solution is to transform your problem until it works for the language.

In this case transforming the problem means transforming a tail problem into a head problem, which seems the only supported operation in pattern matching. It turns that you can easily do that using a list inversion, then work on the reversed list using head elements as you would use tail elements in the original list, and finally, if necessary, revert the result back to initial order (eg. if it was a list).

For example, given a list of integers (eg. [1,2,3,4,5,6]), assume we want to build this list in which every second element of the original list starting from the end is replaced by its double (exercise taken from Homework1 of this excellent introduction to Haskell) : [2,2,6,4,10,6].

Then we can use the following:

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]))

It's obviously much longer than previous solutions, but it feels more Haskell-ish to me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文