列表中最后一个元素的值

22

如何获取列表的最后一个元素的值?我注意到List.hd(或.Head)返回一个项目,而List.tl(或.Tail)返回一个列表。

是否使用rev函数颠倒列表并获取hd是唯一的方法?谢谢。

11个回答

31

试试这个函数。尽管函数使用了递归,但由于是尾递归,所以最终会被优化为迭代。无论如何,这个函数很可能比翻转整个列表(使用 List.rev)更快。

let rec last = function
    | hd :: [] -> hd
    | hd :: tl -> last tl
    | _ -> failwith "Empty list."

Pavel Minaev的答案绝对值得考虑。尽管如此,你所请求的算法在某些罕见情况下可能会有用,并且是完成任务最有效的方法。


2
由于这是一个尾递归算法,编译器将其实现为高效的while循环。我倾向于支持Pavel关于数据结构选择的观点,但如果你需要使用列表,则这是正确的方法。 - dahlbyk
@dahlbyk: 确实。我本想在这方面做个注释的,但看起来我忘了 - 我现在会加上它。 - Noldorin
让rec last tl = function函数没有参数 - Racooon

27

一般来说,如果您需要这样做,那么您正在做错误的事情。由于F#列表是单向链接的,访问最后一个元素是代价高昂的 - O(N),其中N是list的大小。尝试重写您的算法,以便始终访问第一个元素,而不是最后一个(这是O(1))。如果您无法这样做,很可能您在选择数据结构时选择了错误的list


12

一个快速且简单的方法是使用List.reduce。假设列表名称为ls

let lastElement ls = List.reduce (fun _ i -> i) ls

就效率而言,我同意Pavel的看法。


5
基于Mitch的答案,以下是更简洁的版本:
let lastItem = myList |> List.rev |> List.head

myList 列表传递给 List.rev 函数。然后使用 List.head 处理结果。


3

同意,获取list的最后一个元素或其他“可枚举”的序列不够高效。话虽如此,在Seq模块中已经存在这个函数,即Seq.last


即使它没有,也很容易定义:module Seq = let last xs = Seq.reduce (fun _ x -> x) xs - kaefer

1
作为一个初学者F#开发者,我不认为以下操作会有什么害处。
let mylist = [1;2;3;4;5]

let lastValue = mylist.[mylist.Length - 1]

本质上是命令式的吗?是的,但不需要递归。


2
这将需要相当长的时间,因为List.length需要遍历列表,访问也是如此。基本上你必须遍历两次列表。 - John Palmer
1
我刚刚检查了源代码,你是正确的。然而为什么属性计算机会有这个值,对我来说还是个谜。也许当知道元素数量时,我期望列表具有与数组相同的行为。 - Razor
列表不会缓存它们的大小 - 这显著减少了它们在内存中的大小,因为您需要在每个元素处缓存大小。 - John Palmer
你说的"size"是指长度吗?我以为列表拥有一个作为后备存储的数组,当大小超过容量时,它会创建一个新的数组并将元素复制过去。 - Razor
具有数组作为后备存储的列表是System.Collections.Generic.List<_>,它们与F#列表不同,并具有不同的性能目标。 - John Palmer

0
以下代码在我这里运行良好,我有一个整数数组,想从第5项开始,然后减去该项的数字。
Sum of [Array(xi) - Array(xi-5)] where i start at 5

所使用的代码是:

series |> Array.windowed 5
       |> Array.fold (fun s x -> 
                            (x |> Array.rev |> Array.head) -  (x |> Array.head) + s) 0
       |> float

0

我认为你可以直接写

list.[0..list.Length-1]

0

这是一个非常老的问题,但以防万一有人来到这里:

使用 FSharp 5,您可以执行 x.[^index],其中 index 将从数组/列表的末尾开始。

let a = [1;2;3;4;5;6;7;8;9]

a.[^0] is 9
a.[^1] is 8
etc

这是一个不错的功能,但我也想知道它是否会遍历两次列表。当然,这并不意味着这个功能不好,但可能不是检索最后一个元素的最佳选择。 - Bent Tranberg
除非存储列表长度;我还没有查看实现,但这绝对是一个有效的问题。 - Thomas

0
在F#中处理列表的常规方式是使用递归。列表中的第一项是头部(显然),而列表的其余部分是尾部(与最后一项相对)。因此,当函数接收到一个列表时,它会处理头部,然后递归地处理列表的其余部分(即尾部)。
let reversedList = List.rev originalList
let tailItem = List.hd reversedList

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接