从 F# 中具有 N 个不同索引的序列中获取 N 个元素
我是 F# 新手,正在寻找一个采用 N* 索引和序列并给我 N 个元素的函数。如果我有 N 个索引,它应该等于 concat Seq.nth index0, Seq.nth index1 .. Seq.nth indexN 但它应该只扫描序列中的 indexN 元素(O(N)),而不是 index0+index1+.. .+索引N (O(N^2))。
总而言之,我正在寻找类似的东西:
//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# 提供了一个很好的标准方法,我会而是使用它。
谢谢:)...
补充:我做了以下内容。它有效,但不漂亮。欢迎提出建议
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
}
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
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.
Thanks :)...
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
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
当您想通过索引访问元素时,使用序列并不是一个好主意。序列被设计为允许顺序迭代。我会将序列的必要部分转换为数组,然后按索引选择元素:
关于您的实现 - 我认为它不能变得更加优雅。在实现需要以交错方式从多个源获取值的函数时,这是一个普遍问题 - 只是没有优雅的方式来编写这些!
但是,您可以使用递归以函数式方式编写此代码,如下所示:
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:
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:
当你需要扫描一个序列并在O(n)内累积结果时,你总是可以回退到Seq.fold:
这个解决方案的缺点是它会枚举序列到最后,即使如果它已经积累了所有所需的元素。
When you need to scan a sequence and accumulate results in O(n), you can always fall back to Seq.fold:
The drawback of this solution is that it will enumerate the sequence to the end, even if it has accumulated all the desired elements already.
这是我的观点。该解决方案只会按照需要进入序列并以列表形式返回元素。
这里唯一的假设是您提供的索引列表是经过排序的。否则该功能将无法正常工作。
Here is my shot at this. This solution will only go as far as it needs into the sequence and returns the elements as a list.
The only assumption made here is that the index list you supply is sorted. The function won't work properly otherwise.
返回的结果是排序的,是不是有问题?
该算法将在输入序列上线性工作。只需要对索引进行排序即可。如果序列很大,但索引不是很多 - 它会很快。
复杂度为:N -> Max(索引), M ->索引数量:最坏情况下为 O(N + MlogM)。
这是 List.fold 的变体,但阅读起来更复杂。我更喜欢第一个:
附加:仍然比您的变体慢,但比我的旧变体快得多。由于不使用 Seq.skip 创建新的枚举器并且大大减慢了速度。
Is it a problem, that the returned result is sorted?
This algorithm will work linearly over the input sequence. Just the indices need to be sorted. If the sequence is large, but indices are not so many - it'll be fast.
Complexity is: N -> Max(indices), M -> count of indices: O(N + MlogM) in the worst case.
Here is a List.fold variant, but is more complex to read. I prefer the first:
Appended: Still slower than your variant, but a lot faster than mine older variants. Because of not using Seq.skip that is creating new enumerators and was slowing down things a lot.