简体   繁体   English

在F#中,Scala的Seq.Span相当于什么?

[英]What is the equivalent of Scala's Seq.Span in F#?

To quote Scala's documentation : 引用Scala的文档

def span(p: (A) => Boolean): (Seq[A], Seq[A])

Splits this iterable collection into a prefix/suffix pair according to a predicate. 根据谓词将此可迭代集合拆分为前缀/后缀对。

Note: c span p is equivalent to (but possibly more efficient than) (c takeWhile p, c dropWhile p), provided the evaluation of the predicate p does not cause any side-effects. 注意:c span p等效于(但可能更有效)(c takeWhile p,c dropWhile p),前提是谓词p的评估不会引起任何副作用。

Note: might return different results for different runs, unless the underlying collection type is ordered. 注意:除非订购了基础集合类型,否则可能会为不同的运行返回不同的结果。

  • p - the test predicate p - 测试谓词
  • returns - a pair consisting of the longest prefix of this iterable collection whose elements all satisfy p, and the rest of this iterable collection. 返回 - 由此可迭代集合的最长前缀组成的对,其元素全部满足p,以及此可迭代集合的其余部分。
  • Definition Classes - IterableOps → IterableOnceOps 定义类 - IterableOps→IterableOnceOps
  • Note - Reuse: After calling this method, one should discard the iterator it was called on, and use only the iterators that were returned. 注 - 重用:在调用此方法之后,应该丢弃它被调用的迭代器,并仅使用返回的迭代器。 Using the old iterator is undefined, subject to change, and may result in changes to the new iterators as well. 使用旧迭代器是未定义的,可能会发生更改,并且可能会导致对新迭代器进行更改。

When looking at the F# documentation for Seq I don't see any equivalent. 在查看Seq的F#文档时 ,我没有看到任何等效文档

groupBy, partition, splitAt, none of them match what I'm trying to do. groupBy,partition,splitAt,它们都不符合我的目的。 It's like doing a takeWhile and skipWhile at the same time but instead of needing two iterations you only need one where the function would return a tuple of (takeWhile, skipWhile). 这就像在同一时间做一个takeWhile和skipWhile但不需要两次迭代,你只需要一个函数将返回一个元组(takeWhile,skipWhile)。

The output should match the following function 输出应与以下函数匹配

module List
let span (predicate: 'a -> bool) (list: 'a list): ('a list * 'a list) =
    (list |> List.takeWhile predicate, list |> List.skipWhile predicate)

But only need one iteration since my sequence may be infinite. 但是只需要一次迭代,因为我的序列可能是无限的。

[1;2;3;4] |> List.span (fun i -> i % 2 = 1) => ([1], [2;3;4])

Your explanation is not what Seq.span does in Scala: it partitions one sequence in two, putting all input elements into first tuple value as long as predicate function returns true. 你的解释不是Seq.span在Scala中的作用:它将一个序列分成两个,只要谓词函数返回true,就将所有输入元素放入第一个元组值。 Once function returns false, all remaining elements will be pushed into second tuple value. 一旦函数返回false,所有剩余的元素将被推入第二个元组值。

Example in F# would be like: F#中的示例如下:

[1;2;3;4] |> Seq.span (fun i -> i % 2 = 1) => ([1], [2;3;4])

This can be achieved quite easily using mutualy recursive functions: 使用mutualy递归函数可以很容易地实现这一点:

module Seq

open System.Collections.Generic

let span (predicate: 'a -> bool) (seq: 'a seq): ('a seq * 'a seq) =
    let rec insertLeft predicate (e: IEnumerator<'a>) (left: ResizeArray<'a>) (right: ResizeArray<'a>) =
        if e.MoveNext() then
            if predicate e.Current then
                left.Add e.Current
                insertLeft predicate e left right
            else
                // once predicate returned false, all consecutive elements land in right list
                right.Add e.Current 
                insertRight e right
    and insertRight (e: IEnumerator<'a>) (right: ResizeArray<'a>) =
        if e.MoveNext() then 
            right.Add e.Current
            insertRight e right
    let left = ResizeArray<_>()
    let right = ResizeArray<_>()
    use enumerator = seq.GetEnumerator()
    insertLeft predicate enumerator left right
    (upcast left, upcast right)

I have a helper function which I find quite useful for this sort of thing: 我有一个帮助函数,我发现这对于这类事情非常有用:

module Seq =
    let groupAdjacentBy f xs =
        let mutable prevKey, i = None, 0
        xs
        |> Seq.groupBy (fun x ->
            let key = f x
            if prevKey <> Some key then
                i <- i + 1
                prevKey <- Some key
            (i, key))
        |> Seq.map (fun ((_, k), v) -> (k, v))

Note that it uses locally contained mutation in the implementation because it's the simplest way to reuse the existing Seq.groupBy . 请注意,它在实现中使用了本地包含的变异,因为它是重用现有Seq.groupBy的最简单方法。

This is actually a grouping function but it only puts items in the same group if they are adjacent to each other. 这实际上是一个分组函数,但它只是将项目放在同一个组中,如果它们彼此相邻。 To my mind this is a very general way of solving problems that need several uses of takeWhile and skipWhile , but simpler because it's all done in one pass. 在我看来,这是解决需要多次使用takeWhileskipWhile问题的一种非常通用的方法,但更简单,因为它都是在一次传递中完成的。 The grouping function returns a group key of any type rather than just a boolean, adding more flexibility. 分组函数返回任何类型的组键而不仅仅是布尔值,从而增加了更多的灵活性。

Here's an example usage with a grouping function that returns a boolean: 以下是使用返回布尔值的分组函数的示例用法:

[ 1; 2; -1; -2; 3; 4; -5 ]
|> Seq.groupAdjacentBy (fun x -> x > 0) // positive?
|> Seq.map snd
// seq [seq [1; 2]; seq [-1; -2]; seq [3; 4]; seq [-5]]

In this example the first two lines return the groups with their keys ( true , false , true , false respectively). 在此示例中,前两行返回组及其键(分别为truefalsetruefalse )。 You can then use these keys in your logic, but if you don't care about them then the Seq.map snd will discard them. 然后,您可以在逻辑中使用这些键,但如果您不关心它们,则Seq.map snd将丢弃它们。 And you're left with a seq<seq<int>> as shown above. 如上所示,您将获得一个seq<seq<int>>

This is what I've come up with, it's not a great answer because it requires you eagerly iterate through the first one so if you return true long enough you will blow without of memory. 这就是我想出来的,这不是一个很好的答案,因为它需要你急切地遍历第一个,所以如果你回归真的足够长,你将会没有记忆。 I will leave the question open for a few more days and if I don't see a better answer I will mark this one. 我将问题再开放几天,如果我没有看到更好的答案,我会标记这个问题。 Again I would really like a better answer that completely works with an infinite sequence in any situation. 同样,我真的想要一个更好的答案,在任何情况下完全适用于无限序列。

module Seq

let span (predicate: 'a -> bool) (sequence: 'a seq): ('a seq * 'a seq) =
    let enumerator = sequence.GetEnumerator()
    let isNotDone = ref (enumerator.MoveNext())
    let first = seq {
        let e = enumerator
        if !isNotDone then
            while (!isNotDone && predicate e.Current) do
                yield enumerator.Current
                isNotDone := e.MoveNext() }
    let second = seq {
        use e = enumerator
        if !isNotDone then
            yield e.Current
            while e.MoveNext() do
                yield e.Current }
    let eagerFirst = List.toSeq (Seq.toList first)
    (eagerFirst, second)

Looks like you want something like the following, you'd just pass the same Seq (or List or whatever) to filter twice as follows: 看起来你想要类似下面这样的东西,你只需要传递相同的Seq (或List或其他)来filter两次,如下所示:

//Returns a tuple of seq<'a> * seq<'a> (which looks like what you want).
let scalaSpanTuple test sq = (Seq.filter test sq, Seq.filter (test>>not) sq)

or: 要么:

//Returns a list of 2 lists of 'a
let scalaSpanList test ls = [List.filter test ls; List.filter (test>>not) ls]

etc. 等等

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM