[英]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的評估不會引起任何副作用。
注意:除非訂購了基礎集合類型,否則可能會為不同的運行返回不同的結果。
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
的最簡單方法。
這實際上是一個分組函數,但它只是將項目放在同一個組中,如果它們彼此相鄰。 在我看來,這是解決需要多次使用takeWhile
和skipWhile
問題的一種非常通用的方法,但更簡單,因為它都是在一次傳遞中完成的。 分組函數返回任何類型的組鍵而不僅僅是布爾值,從而增加了更多的靈活性。
以下是使用返回布爾值的分組函數的示例用法:
[ 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]]
在此示例中,前兩行返回組及其鍵(分別為true
, false
, true
和false
)。 然后,您可以在邏輯中使用這些鍵,但如果您不關心它們,則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.