从 F# 中的列表中提取单个元素

发布于 2024-07-19 03:33:26 字数 963 浏览 5 评论 0原文

我想从 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."))

我想它更好一点。

I want to extract a single item from a sequence in F#, or give an error if there is none or more than one. What is the best way to do this?

I currently have

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."))

It seems to work, but is it really the best way?

Edit: As I was pointed in the right direction, I came up with the following:

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."))

I guess it's a little nicer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

独木成林 2024-07-26 03:33:27

序列有查找功能。

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

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

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

Sequence has a find function.

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

but if you want to ensure that the seq has only one element, then doing a Seq.filter, then take the length after filter and ensure it equals one, and then take the head. All in Seq, no need to convert to a list.

Edit:
On a side note, I was going to suggest checking that the tail of a result is empty (O(1), instead of using the function length (O(n)). Tail isn't a part of seq, but I think you can work out a good way to emulate that functionality.

别忘他 2024-07-26 03:33:27

以现有序列标准函数的风格完成

#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

您可以做一个纯粹的实现,但它最终会跳过圈子以确保正确和高效(在第二场比赛中快速终止实际上需要一个标志说“我已经找到了它”)

done in the style of the existing sequence standard functions

#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

You could do a pure implementation but it will end up jumping through hoops to be correct and efficient (terminating quickly on the second match really calls for a flag saying 'I found it already')

灰色世界里的红玫瑰 2024-07-26 03:33:27

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

Updated answer would be to use Seq.exactlyOne which raises an ArgumentException

夜巴黎 2024-07-26 03:33:27

使用现有的库函数有什么问题吗?

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

[1;2;3] |> single ((=) 4)

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)
叹沉浮 2024-07-26 03:33:27

用这个:

> 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

Use this:

> 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
偏爱自由 2024-07-26 03:33:27

我的两分钱...这适用于选项类型,所以我可以在我的自定义也许 monad 中使用它。 可以很容易地修改,但可以使用异常

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

My two cents... this works with the option type so I can use it in my custom maybe monad. could be modified very easy though to work with exceptions instead

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文