如何从f#列表中取出最后N项?

时间:2021-08-26 19:41:15

I would do something like

我会做一些类似的事情

let last n xs = xs |> List.rev |> Seq.take n |> List.ofSeq |> List.rev

I am not sure about turning a list to a sequence and back though. Is this how you do it F#?

我不确定是否要把一个列表转换成一个序列,然后再返回。你就是这样做的吗?

4 个解决方案

#1


3  

You could use List.foldBack to traverse the list from the end:

您可以使用列表。回到最后遍历列表:

let takeLast n list = 
    let (_, r) = List.foldBack (fun e (i, acc) -> (i - 1, if i <= 0 then acc else e :: acc)) list (n, [])
    r

#2


3  

Seq + Skip

Taking the last N items is equivalent to skipping the first (length - N) items, so for a Sequence as input (and output), you could do something like:

取最后的N项相当于跳过第一个(长度- N)项,因此对于一个序列作为输入(和输出),您可以做如下操作:

let last n xs = Seq.skip ((Seq.length xs) - n) xs

(or, with piping, let last n xs = xs |> Seq.skip (Seq.length xs - n)

(或者,有管道,让n xs = xs |> Seq。跳过(Seq。长度xs - n)

and for a List as input (and output) you could do:

对于作为输入(和输出)的列表,您可以这样做:

let last n xs = List.toSeq xs |> Seq.skip (xs.Length - n) |> Seq.toList

or by defining both, just pipe it to the sequence one:

或者通过定义两者,把它们连接到序列1:

let lastList n xs = List.toSeq xs |> last n |> Seq.toList

Tail + Recursion

Alternatively, this can be achieved by (tail) recursively applying Tail as so:

或者,这可以通过(tail)递归地应用tail来实现:

let rec last n xs =
  if List.length xs <= n then xs
  else last n xs.Tail

#3


0  

To avoid rebuilding the list, you may use a simple recursive algorithm.

为了避免重新构建列表,您可以使用一个简单的递归算法。

Note, we are not using neither List.Cons nor Seq.toList which does the same internally.

注意,我们没有使用这两个列表。缺点和Seq。toList在内部做同样的事情。

let lastN n xs =
    let rec skip n xs = 
        match n, xs with
        | _, []     -> []   // empty list, returning unchanged
        | 0, _      -> xs   // found an element at which the remainder
                            // of the list is to be returned
        | n', h::t  -> skip (n-1) t    // proceed to next iteration

    let toSkip = (List.length xs) - n  // how many elements to skip
    if toSkip < 0 then xs   // or an exception, depending on expected behavior
    elif toSkip = 0 then xs // requested exactly as many elements
                            // as the list contains
    else skip toSkip xs

// usage
let data = [1 .. 10000000]
let stopWatch = new System.Diagnostics.Stopwatch()
stopWatch.Start()
data
|> lastN 3
|> List.iter (printf "%d ")
stopWatch.Stop()
printfn "\nelapsed: %f ms" stopWatch.Elapsed.TotalMilliseconds

Output:

输出:

9999998 9999999 10000000
elapsed: 194.846700 ms

#4


0  

Variation on chamila_c's function:-

变化在chamila_c功能:-

/// Returns the last abs(n) items in the specified sequence.
let lastN n xs =
    // The number to skip will be negative if n is too large; this will result in 0 items being skipped.
    // By taking abs(n), the number to skip can't get too large, and we avoid an exception being thrown.
    xs |> Seq.skip (Seq.length xs - abs n)

#1


3  

You could use List.foldBack to traverse the list from the end:

您可以使用列表。回到最后遍历列表:

let takeLast n list = 
    let (_, r) = List.foldBack (fun e (i, acc) -> (i - 1, if i <= 0 then acc else e :: acc)) list (n, [])
    r

#2


3  

Seq + Skip

Taking the last N items is equivalent to skipping the first (length - N) items, so for a Sequence as input (and output), you could do something like:

取最后的N项相当于跳过第一个(长度- N)项,因此对于一个序列作为输入(和输出),您可以做如下操作:

let last n xs = Seq.skip ((Seq.length xs) - n) xs

(or, with piping, let last n xs = xs |> Seq.skip (Seq.length xs - n)

(或者,有管道,让n xs = xs |> Seq。跳过(Seq。长度xs - n)

and for a List as input (and output) you could do:

对于作为输入(和输出)的列表,您可以这样做:

let last n xs = List.toSeq xs |> Seq.skip (xs.Length - n) |> Seq.toList

or by defining both, just pipe it to the sequence one:

或者通过定义两者,把它们连接到序列1:

let lastList n xs = List.toSeq xs |> last n |> Seq.toList

Tail + Recursion

Alternatively, this can be achieved by (tail) recursively applying Tail as so:

或者,这可以通过(tail)递归地应用tail来实现:

let rec last n xs =
  if List.length xs <= n then xs
  else last n xs.Tail

#3


0  

To avoid rebuilding the list, you may use a simple recursive algorithm.

为了避免重新构建列表,您可以使用一个简单的递归算法。

Note, we are not using neither List.Cons nor Seq.toList which does the same internally.

注意,我们没有使用这两个列表。缺点和Seq。toList在内部做同样的事情。

let lastN n xs =
    let rec skip n xs = 
        match n, xs with
        | _, []     -> []   // empty list, returning unchanged
        | 0, _      -> xs   // found an element at which the remainder
                            // of the list is to be returned
        | n', h::t  -> skip (n-1) t    // proceed to next iteration

    let toSkip = (List.length xs) - n  // how many elements to skip
    if toSkip < 0 then xs   // or an exception, depending on expected behavior
    elif toSkip = 0 then xs // requested exactly as many elements
                            // as the list contains
    else skip toSkip xs

// usage
let data = [1 .. 10000000]
let stopWatch = new System.Diagnostics.Stopwatch()
stopWatch.Start()
data
|> lastN 3
|> List.iter (printf "%d ")
stopWatch.Stop()
printfn "\nelapsed: %f ms" stopWatch.Elapsed.TotalMilliseconds

Output:

输出:

9999998 9999999 10000000
elapsed: 194.846700 ms

#4


0  

Variation on chamila_c's function:-

变化在chamila_c功能:-

/// Returns the last abs(n) items in the specified sequence.
let lastN n xs =
    // The number to skip will be negative if n is too large; this will result in 0 items being skipped.
    // By taking abs(n), the number to skip can't get too large, and we avoid an exception being thrown.
    xs |> Seq.skip (Seq.length xs - abs n)