def span(p: (A) => Boolean): (Seq[A], Seq[A])
根据谓词将此可迭代集合拆分为前缀/后缀对。
请注意:只要对谓词p的求值没有引起任何副作用,c span p等效于(但可能比c跨度更有效),但对谓词p的评估不会造成任何副作用。
注意:对于不同的运行,可能会返回不同的结果,除非已订购基础集合类型。
查看F# documentation的Seq时,看不到任何等效内容。
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])
答案 0 :(得分:3)
您的解释不是Seq.span
在Scala中的作用:它将一个序列一分为二,只要谓词函数返回true,就将所有输入元素放入第一个元组值。一旦函数返回false,所有剩余元素将被推入第二个元组值。
F#中的示例如下:
[1;2;3;4] |> Seq.span (fun i -> i % 2 = 1) => ([1], [2;3;4])
使用相互递归函数可以很容易地实现这一点:
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)
答案 1 :(得分:2)
我有一个辅助函数,对于这种事情我觉得非常有用:
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>>
。
答案 2 :(得分:1)
这是我想出的,这不是一个很好的答案,因为它要求您急切地迭代第一个,所以如果您返回true足够长的时间,您将失去记忆。我将让问题再开放几天,如果没有更好的答案,我会标记为这个问题。再一次,我真的希望有一个更好的答案,它在任何情况下都可以与无限序列完全兼容。
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)
答案 3 :(得分:0)
看起来您想要以下内容,只需将相同的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]
等