F# で Seq.takeWhile + 1 つの項目を実行する方法 質問する

F# で Seq.takeWhile + 1 つの項目を実行する方法 質問する

述語を使用してシーケンスをフィルタリングする関数を記述したいのですが、その結果には述語が false を返す最初の項目も含める必要があります。

F#にbreakキーワードがあった場合、ロジックは次のようになります。

let myFilter predicate s =
    seq {
        for item in s do
            yield item
            if predicate item then
                break
    }

Seq.takeWhile と Seq.skipWhile の組み合わせを試してみました。次のようなものです:

Seq.append 
    (Seq.takeWhile predicate s) 
    (Seq.skipWhile predicate s |> Seq.take 1)

...しかし、問題は述語に一致する最初の項目がtakeWhileとskipWhileの間で失われることです。

また、入力シーケンスは遅延であるため、シーケンスを消費して後で決定を下すソリューションは実行できないことに注意してください。

何か案は?

ありがとう!

編集: たくさんの回答をありがとうございました! こんなに早くたくさんの回答が来るとは思っていませんでした。すぐにそれぞれ見ていきます。ここでもう少し背景を説明したいと思います。シェルを実装する次のコーディング カタを考えてみましょう。

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

この実装には問題がある。しないコマンド q が入力されると、「Good bye!」と出力されます。

私がやりたいのは、機能を実装することです終了するまで処理「q」まですべてのコマンドを処理するようにする。含む「q」。

ベストアンサー1

計算式でのサポートが不足しているのはbreak少し困りものです。これは F# で使用されるモデルにはあま​​り適合しません (これがサポートされていない理由です) が、この場合には非常に役立ちます。

これをシーケンスの1回の反復だけで実装したい場合、最もクリーンな解決策は、シーケンスの基礎となる構造を使用して、再帰ループとして記述することだと思います。IEnumerator<'T>

これはかなり短く(ここにある他のソリューションと比較して)、非常に明確なコードでもあります。

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 }

おすすめ記事