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

以现有序列标准函数的样式完成

#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

你可以做一个纯粹的实现,但它最终会跳过箍以保持正确和高效(在第二场比赛中快速终止真的需要一个标志说'我已经找到它了')

于 2009-05-05T17:36:32.567 回答
4

序列具有查找功能。

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

但是如果你想确保seq只有一个元素,那么做一个Seq.filter,然后取filter后的长度并确保它等于1,然后取head。全部在 Seq 中,无需转换为列表。

编辑:在旁注中,我建议检查结果的尾部是否为空(O(1),而不是使用函数length(O(n))。尾部不是 seq 的一部分,但我认为您可以找到一种模拟该功能的好方法。

于 2009-05-05T16:54:00.000 回答
2

What's wrong with using the existing library function?

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

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

更新的答案是使用 Seq.exactlyOne 引发 ArgumentException

于 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

我的两分钱......这适用于选项类型,所以我可以在我的自定义可能单子中使用它。可以很容易地修改,但可以使用异常来代替

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 回答