从F#中的N个不同索引的序列中取N个元素 [英] Take N elements from sequence with N different indexes in F#
问题描述
我是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屋!