7

F#のシーケンスから単一のアイテムを抽出するか、存在しないか複数ある場合はエラーを出します。これを行うための最良の方法は何ですか?

私は現在持っています

let element = data |> (Seq.filter (function | RawXml.Property (x) -> false | _ -> true))
                   |> List.of_seq
                   |> (function head :: [] -> head | head :: tail -> failwith("Too many elements.") | [] -> failwith("Empty sequence"))
                   |> (fun x -> match x with MyElement (data) -> x | _ -> failwith("Bad element."))

それはうまくいくようですが、それは本当に最良の方法ですか?

編集:私は正しい方向に向けられたので、私は次のことを思いついた:

let element = data |> (Seq.filter (function | RawXml.Property (x) -> false | _ -> true))
                   |> (fun s -> if Seq.length s <> 1 then failwith("The sequence must have exactly one item") else s)
                   |> Seq.hd
                   |> (fun x -> match x with MyElement (_) -> x | _ -> failwith("Bad element."))

もう少しいいと思います。

4

6 に答える 6

4

シーケンスには検索機能があります。

val find : ('a -> bool) -> seq<'a> -> 'a

ただし、seqに要素が1つしかないことを確認する場合は、Seq.filterを実行し、フィルター後の長さを取得して1に等しいことを確認してから、ヘッドを取得します。すべてSeqで、リストに変換する必要はありません。

編集:補足として、結果のテールが空であること確認することを提案しました(関数(O(n)を使用する代わりにO(1) )。テールはseqの一部ではありませんが、私はその機能をエミュレートするための良い方法を考え出すことができると思います。length

于 2009-05-05T16:54:00.000 に答える
4

既存のシーケンス標準関数のスタイルで実行されます

#light

let findOneAndOnlyOne f (ie : seq<'a>)  = 
    use e = ie.GetEnumerator()
    let mutable res = None 
    while (e.MoveNext()) do
        if f e.Current then
            match res with
            | None -> res <- Some e.Current
            | _ -> invalid_arg "there is more than one match"          
    done;
    match res with
        | None -> invalid_arg "no match"          
        | _ -> res.Value

純粋な実装を行うこともできますが、正しく効率的になるようにフープを飛び越えてしまいます(2番目の試合ですぐに終了するには、「すでに見つかりました」というフラグが必要です)。

于 2009-05-05T17:36:32.567 に答える
2

既存のライブラリ関数を使用することの何が問題になっていますか?

let single f xs = System.Linq.Enumerable.Single(xs, System.Func<_,_>(f))

[1;2;3] |> single ((=) 4)
于 2011-11-24T15:07:54.760 に答える
2

更新された答えは、ArgumentExceptionを発生させるSeq.exactlyOneを使用することです。

于 2016-05-04T11:02:49.500 に答える
1

これを使って:

> let only s =
    if not(Seq.isEmpty s) && Seq.isEmpty(Seq.skip 1 s) then
      Seq.hd s
    else
      raise(System.ArgumentException "only");;
val only : seq<'a> -> 'a
于 2009-05-22T12:01:39.597 に答える
0

私の2セント...これはオプションタイプで機能するので、カスタムのモナドで使用できます。代わりに例外を使用して動作するものの、非常に簡単に変更できます

let Single (items : seq<'a>) =
    let single (e : IEnumerator<'a>) =
        if e.MoveNext () then
            if e.MoveNext () then
                raise(InvalidOperationException "more than one, expecting one")
            else
                Some e.Current
        else
            None
    use e = items.GetEnumerator ()
    e |> single
于 2011-11-23T21:10:00.247 に答える