F# 如何执行Seq.takeWhile+;F中的一项#

F# 如何执行Seq.takeWhile+;F中的一项#,f#,functional-programming,lazy-sequences,F#,Functional Programming,Lazy Sequences,我想编写一个函数,它使用谓词过滤序列,但结果还应该包括谓词返回false的第一项 如果F中有break关键字,那么逻辑会是这样的# 我尝试了Seq.takeWhile和Seq.skipWhile的组合,比如: Seq.append (Seq.takeWhile predicate s) (Seq.skipWhile predicate s |> Seq.take 1) …但问题是,与谓词匹配的第一项在takeWhile和skipWhile之间丢失 还要注意的是,输入序

我想编写一个函数,它使用谓词过滤序列,但结果还应该包括谓词返回false的第一项

如果F中有break关键字,那么逻辑会是这样的#

我尝试了Seq.takeWhile和Seq.skipWhile的组合,比如:

Seq.append 
    (Seq.takeWhile predicate s) 
    (Seq.skipWhile predicate s |> Seq.take 1)
…但问题是,与谓词匹配的第一项在takeWhile和skipWhile之间丢失

还要注意的是,输入序列是惰性的,因此任何使用该序列并随后作出决定的解决方案都是不可行的

有什么想法吗

谢谢

编辑:非常感谢所有的答案!我没想到会有这么多人反应这么快。我很快会逐一查看。现在我只想多讲一点背景。考虑下面的编码实现一个shell的KATA:

let cmdProcessor state = function
    | "q" -> "Good bye!"
    | "h" -> "Help content"
    | c -> sprintf "Bad command: '%s'" c

let processUntilQuit =
    Seq.takeWhile (fun cmd -> cmd <> "q")

let processor = 
    processUntilQuit
    >> Seq.scan cmdProcessor "Welcome!"

module io =
    let consoleLines = seq { while true do yield System.Console.ReadLine () }

    let display : string seq -> unit = Seq.iter <| printfn "%s" 

io.consoleLines |> processor|> io.display

printf "Press any key to continue..."
System.Console.ReadKey ()|> ignore
let cmdProcessor state=函数
|“q”->“再见!”
|“h”->“帮助内容”
|c->sprintf“错误命令:“%s”c
让进程退出=
Seq.takeWhile(趣味指令->指令“q”)
让处理器=
processUntilQuit
>>Seq.scan cmdProcessor“欢迎!”
模块io=
让consoleLines=seq{而true则产生System.Console.ReadLine()}
let display:string seq->unit=seq.iter处理器|>io.display
printf“按任意键继续…”
System.Console.ReadKey()|>忽略
此实现存在的问题是,当输入命令q时,不会打印“再见!”


我想做的是实现函数processUntilQuit,这样它可以处理所有命令直到“q”,包括“q”。

丑陋的非功能解决方案

let myfilter f s =
    let failed = ref false
    let newf = fun elem -> match !failed with 
                           |true -> 
                               failed := f elem
                               true
                           |false->false
    Seq.takeWhile newf s
丑陋的功能解决方案:):

最后是tuple,其中第一个元素包含选项,第一个元素包含源列表中的第一个不匹配元素,第二个元素包含过滤列表

丑陋的功能性懒惰解决方案(对不起,我第一次没有正确阅读您的帖子):

我的F#不够流利,无法让比赛中的终止案例看起来很好,也许一些F#大师会帮上忙

编辑:不那么难看,纯粹的F#解决方案灵感来自托马斯·佩特里切克答案:

let myFilterLazy2 predicate s =
        let rec inner ss = seq {
            if Seq.isEmpty ss = false then
                yield ss |> Seq.head
                if ss |> Seq.head |> predicate then
                    yield! ss |> Seq.skip 1 |> inner
        }

        inner s
如果您不喜欢此处的计算表达式,请选择
duplicateHead
的替代版本:

let duplicateHead' xs =
    Seq.append 
        (Seq.head xs)
        xs
这种方法基于构建当前和下一个元素的元组。
谓词
正在应用于当前元素,但返回以下元素


注意:当
谓词
在第一个元素上失败时,安全。为了使其正常工作,您必须重新工作
duplicateHead
,方法是添加一个元素,该元素肯定会通过
谓词

但您的解决方案并没有真正了解问题所在

两个小的修正:

(1) 使用序列表达式以提高可读性

(2) 如果输入序列为空,则使用
Seq.truncate
代替
Seq.take

let myFilter predicate s = 
    seq { yield! Seq.takeWhile predicate s
          yield! s |> Seq.skipWhile predicate |> Seq.truncate 1 }
好一点的。:)

这一个需要一个额外的参数
n
,它定义了要添加的额外元素的数量


注意:小心使用单行
padWithTrue
done
关键字)

计算表达式中缺少对
break
的支持有点烦人。它与F#使用的模型不太吻合(这就是为什么它不受支持的原因),但在这种情况下它将非常有用


如果您只想在序列上使用一次迭代来实现这一点,那么我认为最干净的解决方案是只使用序列的底层结构,并使用
IEnumerator将其作为递归循环来编写,我猜您希望它做些什么,直到:

let takeUntil pred s =
  let state = ref true
  Seq.takeWhile (fun el ->
    let ret= !state
    state := not <| pred el
    ret
    ) s
让我们继续=
让state=ref为真
Seq.takeWhile(乐趣el->
让ret=!state

状态:=不这是非常古老的,但我认为我会有所贡献,因为其他解决方案没有提出这一点

使用
Seq.scan
来建立谓词结果的两个元素堆栈,并在表示前一个元素的谓词结果的堆栈底部为
true
时简单地执行(注意,尚未测试此代码)


我知道这是一个老问题,但有一个更实用的解决方案

尽管,老实说,对于这个问题我更喜欢


在倒数第二行,
|>Seq.filter Option.isSome
可以替换为
|>Seq.tail
,因为除了
初始状态
之外,没有其他状态匹配
一些(无,|)

另一个迟来的答案,但它是“功能性的”,简单,并且不会读取结果序列中超过最后一个的任何元素

让myFilter谓词=
Seq.collect(乐趣x->[Choice1Of2 x;Choice2Of2(谓词x)])
>>Seq.takeWhile(函数|选择1of2 |->真|选择2of2 p->p)
>>Seq.choose(函数| Choice1Of2 x->Some x | Choice2Of2 |->无)

我知道这个问题提出后的年代,但我不得不处理类似但更一般的问题,我希望有人会发现我的解决方案有用

其思想是在闭包中捕获枚举数,然后返回一个遍历原始序列其余部分的函数

更一般的例子

/// usage example:
let a = seq [1; 2; 3; 4; 5; 6; 7]

use seq = new ContinueSequence<_>(a)
let s1 = seq.Continue(false) |> Seq.takeWhile((>) 3) // take 1 and 2, 3 is current
let s2 = seq.Continue(true) |> Seq.take(2)    // take 3 and 4
let s3 = seq.Continue(false) |> Seq.skip(1)   // skip 5

let s = 
    s1 
    |> Seq.append <| s2 
    |> Seq.append <| s3 
    |> Seq.toList

// s = [1; 2; 3; 4; 6; 7]
///用法示例:
设a=seq[1;2;3;4;5;6;7]
使用顺序=新的连续顺序(a)
设s1=seq.Continue(false)|>seq.takeWhile(>)3//取1和2,3是当前值
设s2=seq.Continue(true)|>seq.take(2)//take 3和4
设s3=seq.Continue(false)|>seq.skip(1)//skip 5
让我们=
s1
|>Seq.append Seq.append Seq.toList
//s=[1;2;3;4;6;7]
这个问题的意思是,与谓词匹配的第一项在takeWhile和ski之间丢失了
let duplicateHead' xs =
    Seq.append 
        (Seq.head xs)
        xs
let myFilter predicate s = 
    seq { yield! Seq.takeWhile predicate s
          yield! s |> Seq.skipWhile predicate |> Seq.truncate 1 }
let padWithTrue n xs = seq { for _ in 1..n do yield true; done; yield! xs }
let filter predicate n xs =
    let ys = xs |> Seq.map predicate |> padWithTrue n
    Seq.zip xs ys
    |> Seq.takeWhile snd
    |> Seq.map fst
let myFilter predicate (s:seq<_>) = 
  /// Iterates over the enumerator, yielding elements and
  /// stops after an element for which the predicate does not hold
  let rec loop (en:IEnumerator<_>) = seq {
    if en.MoveNext() then
      // Always yield the current, stop if predicate does not hold
      yield en.Current
      if predicate en.Current then
        yield! loop en }

  // Get enumerator of the sequence and yield all results
  // (making sure that the enumerator gets disposed)
  seq { use en = s.GetEnumerator()
        yield! loop en }
let takeUntil pred s =
  let state = ref true
  Seq.takeWhile (fun el ->
    let ret= !state
    state := not <| pred el
    ret
    ) s
Seq.scan (fun (a,b,v) e -> (pred e, a, Some e)) (true, true, None )
>> Seq.takeWhile (fun (_,b,_) -> b)
>> Seq.map (fun (_,_,c) -> c)
let takeWhileAndNext predicate mySequence =
    let folder pred state element =
        match state with
            | Some (_, false) ->
                None
            | _ ->
                Some (Some element, pred element)
    let initialState = Some (None, true)
    Seq.scan (folder predicate) initialState mySequence |> Seq.takeWhile Option.isSome
                                                        |> Seq.map Option.get
                                                        |> Seq.map fst
                                                        |> Seq.filter Option.isSome
                                                        |> Seq.map Option.get
/// allows fetching elements from same sequence
type ContinueSequence<'a> (xs: 'a seq) =

    let en = xs.GetEnumerator()

    member _.Continue (includeCurrent: bool) =
        let s = seq { while en.MoveNext() do yield en.Current }
        let c = seq { en.Current }
        if includeCurrent then
            Seq.append c s
        else
            s

    interface IDisposable with 
        member _.Dispose() =
            en.Dispose()
use seq = new ContinueSequence a 
let result = Seq.append
   seq.Continue(false) |> Seq.takeWhile(predicate) 
   seq.Continue(true) |> Seq.take(1)  //include element which breaks predicate
/// usage example:
let a = seq [1; 2; 3; 4; 5; 6; 7]

use seq = new ContinueSequence<_>(a)
let s1 = seq.Continue(false) |> Seq.takeWhile((>) 3) // take 1 and 2, 3 is current
let s2 = seq.Continue(true) |> Seq.take(2)    // take 3 and 4
let s3 = seq.Continue(false) |> Seq.skip(1)   // skip 5

let s = 
    s1 
    |> Seq.append <| s2 
    |> Seq.append <| s3 
    |> Seq.toList

// s = [1; 2; 3; 4; 6; 7]