从F#中的N个不同索引的序列中取N个元素 [英] Take N elements from sequence with N different indexes in F#

查看:123
本文介绍了从F#中的N个不同索引的序列中取N个元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是F#的新手,正在寻找一个带N *个索引和序列的函数,并给出了N个元素。如果我有N个索引它应该等于concat Seq.nth index0,Seq.nth index1 .. Seq.nth indexN但它应该只扫描序列中的indexN元素(O(N))而不是index0 + index1 + .. 。+ indexN(O(N ^ 2))。

I'm new to F# and looking for a function which take N*indexes and a sequence and gives me N elements. If I have N indexes it should be equal to concat Seq.nth index0, Seq.nth index1 .. Seq.nth indexN but it should only scan over indexN elements (O(N)) in the sequence and not index0+index1+...+indexN (O(N^2)).

总之,我正在寻找类似的东西:

To sum up, I'm looking for something like:

//For performance, the index-list should be ordered on input, be padding between elements instead of indexes or be ordered when entering the function
seq {10 .. 20} |> Seq.takeIndexes [0;5;10] 
Result: 10,15,20

我可以通过使用seq {yield ...}并使用一个索引计数器来勾选一些元素应该被传递掉但是如果F#提供了一个很好的标准方法我宁愿使用它。

I could make this by using seq { yield... } and have a index-counter to tick when some element should be passed out but if F# offers a nice standard way I would rather use that.

谢谢:)...

添加:我做了以下内容。它有效但不漂亮。欢迎提出建议

Addition: I have made the following. It works but ain't pretty. Suggestions is welcomed

let seqTakeIndexes (indexes : int list) (xs : seq<int>) =
    seq {
        //Assume indexes is sorted
        let e = xs.GetEnumerator()
        let i = ref indexes 
        let curr = ref 0

        while e.MoveNext() && not (!i).IsEmpty do
            if !curr = List.head !i then
                i := (!i).Tail
                yield e.Current

            curr := !curr + 1
    }


推荐答案

如果要按索引访问元素,那么使用序列并不是一个好主意。序列旨在允许顺序迭代。我会将序列的必要部分转换为数组,然后按索引选择元素:

When you want to access elements by index, then using sequences isn't as good idea. Sequences are designed to allow sequential iteration. I would convert the necessary part of the sequence to an array and then pick the elements by index:

let takeIndexes ns input = 
  // Take only elements that we need to access (sequence could be infinite)
  let arr = input |> Seq.take (1 + Seq.max ns) |> Array.ofSeq
  // Simply pick elements at the specified indices from the array
  seq { for index in ns -> arr.[index] }

seq [10 .. 20] |> takeIndexes [0;5;10]  

关于你的实施 - 我不认为它可以被制作显得更优雅。当实现需要以交错方式从多个源中获取值的函数时,这是一个普遍的问题 - 没有优雅的方法来编写它们!

Regarding your implementation - I don't think it can be made significantly more elegant. This is a general problem when implementing functions that need to take values from multiple sources in an interleaved fashion - there is just no elegant way of writing those!

但是,你可以使用这样的递归以函数方式写这个:

However, you can write this in a functional way using recursion like this:

let takeIndexes indices (xs:seq<int>) = 
  // Iterates over the list of indices recursively
  let rec loop (xe:IEnumerator<_>) idx indices = seq {
    let next = loop xe (idx + 1)
    // If the sequence ends, then end as well
    if xe.MoveNext() then
      match indices with
      | i::indices when idx = i -> 
        // We're passing the specified index 
        yield xe.Current
        yield! next indices
      | _ -> 
        // Keep waiting for the first index from the list
        yield! next indices }
  seq {
    // Note: 'use' guarantees proper disposal of the source sequence
    use xe = xs.GetEnumerator()
    yield! loop xe 0 indices }

seq [10 .. 20] |> takeIndexes [0;5;10]  

这篇关于从F#中的N个不同索引的序列中取N个元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆