簡體   English   中英

在F#中,Scala的Seq.Span相當於什么?

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

引用Scala的文檔

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

根據謂詞將此可迭代集合拆分為前綴/后綴對。

注意:c span p等效於(但可能更有效)(c takeWhile p,c dropWhile p),前提是謂詞p的評估不會引起任何副作用。

注意:除非訂購了基礎集合類型,否則可能會為不同的運行返回不同的結果。

  • p - 測試謂詞
  • 返回 - 由此可迭代集合的最長前綴組成的對,其元素全部滿足p,以及此可迭代集合的其余部分。
  • 定義類 - IterableOps→IterableOnceOps
  • 注 - 重用:在調用此方法之后,應該丟棄它被調用的迭代器,並僅使用返回的迭代器。 使用舊迭代器是未定義的,可能會發生更改,並且可能會導致對新迭代器進行更改。

在查看Seq的F#文檔時 ,我沒有看到任何等效文檔

groupBy,partition,splitAt,它們都不符合我的目的。 這就像在同一時間做一個takeWhile和skipWhile但不需要兩次迭代,你只需要一個函數將返回一個元組(takeWhile,skipWhile)。

輸出應與以下函數匹配

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

但是只需要一次迭代,因為我的序列可能是無限的。

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

你的解釋不是Seq.span在Scala中的作用:它將一個序列分成兩個,只要謂詞函數返回true,就將所有輸入元素放入第一個元組值。 一旦函數返回false,所有剩余的元素將被推入第二個元組值。

F#中的示例如下:

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

使用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)

我有一個幫助函數,我發現這對於這類事情非常有用:

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))

請注意,它在實現中使用了本地包含的變異,因為它是重用現有Seq.groupBy的最簡單方法。

這實際上是一個分組函數,但它只是將項目放在同一個組中,如果它們彼此相鄰。 在我看來,這是解決需要多次使用takeWhileskipWhile問題的一種非常通用的方法,但更簡單,因為它都是在一次傳遞中完成的。 分組函數返回任何類型的組鍵而不僅僅是布爾值,從而增加了更多的靈活性。

以下是使用返回布爾值的分組函數的示例用法:

[ 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]]

在此示例中,前兩行返回組及其鍵(分別為truefalsetruefalse )。 然后,您可以在邏輯中使用這些鍵,但如果您不關心它們,則Seq.map snd將丟棄它們。 如上所示,您將獲得一個seq<seq<int>>

這就是我想出來的,這不是一個很好的答案,因為它需要你急切地遍歷第一個,所以如果你回歸真的足夠長,你將會沒有記憶。 我將問題再開放幾天,如果我沒有看到更好的答案,我會標記這個問題。 同樣,我真的想要一個更好的答案,在任何情況下完全適用於無限序列。

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)

看起來你想要類似下面這樣的東西,你只需要傳遞相同的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)

要么:

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

等等

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM