【问题标题】:Accessing items in an IEnumerable in F#在 F# 中访问 IEnumerable 中的项目
【发布时间】:2011-08-27 15:23:00
【问题描述】:

我正在尝试在 F# 中使用 Youtube .Net API,但在尝试访问返回给 userPlaylists.Entries 属性的 IEnumerable 时遇到了问题。下面是我测试过的 c# 代码,但是我似乎无法从 f# 中的 IEnumerable 集合中返回单个项目。

 Feed<Playlist> userPlaylists = request.GetPlaylistsFeed("username");
 var p = userPlaylists.Entries.Single<Playlist>(x => x.Title == "playlistname");

Entries 解析为 IEnumerable&lt;Playlist&gt; Feed&lt;PlayList&gt;.Entries 类型

IEnumerable 似乎在 F# 中表示为一个序列,但我似乎无法弄清楚如何返回正确类型的单个项目,我得到的最接近的是:

let UserPlaylists = Request.GetPlaylistsFeed("username")
let pl = UserPlaylists.Entries |>
Seq.tryPick(fun x -> if x.Title="playlistname" then Some(x) else None)

但是,这似乎返回一种“播放列表选项”而不是“播放列表”。谁能建议从 IEnumerable 中检索单个项目的正确方法?

【问题讨论】:

  • 如果在seq中找不到想要的item怎么办?

标签: c# f# youtube


【解决方案1】:

Single() 扩展方法如果找不到匹配项则抛出异常,如果找到多个匹配项也会抛出异常。 F# Seq 模块中没有直接等效项。最接近的是Seq.find,就像.Single() 一样,如果找不到匹配项会抛出异常,但与.Single() 不同的是,它会在找到匹配项后立即停止查找,如果更多则不会抛出错误存在不止一个匹配项。

如果您确实需要“如果匹配多个则抛出错误”行为,那么最简单的方法是使用 F# 中的确切方法:

open System.Linq

let p = userPlaylists.Entries.Single(fun x -> x.Title = "playlistname")

如果您不需要“如果有多个匹配项则抛出”行为,那么您也不应该在 C# 代码中使用 .Single() - .First() 会表现得更好,因为它会立即停止查找它找到了一个匹配项。

如果是我,为了简洁,我会使用 Seq.tryFind 而不是 Seq.tryPick,并处理“未找到”的情况而不是抛出错误。

let userPlaylists = request.GetPlaylistsFeed("username")
let p = userPlaylists.Entries |> Seq.tryFind (fun x -> x.Title = "playlistname")
match p with
| Some pl ->
    // do something with the list
| None ->
    // do something else because we didn't find the list

或者,我可以这样做而不创建只被引用一次的中间值......

request.GetPlaylistsFeed("username").Entries
|> Seq.tryFind (fun x -> x.Title = "playlistname")
|> function
   | Some pl ->
       // do something with the list
   | None ->
       // do something else because we didn't find the list

我真的很喜欢管道运营商...

【讨论】:

    【解决方案2】:

    Seq.tryPick 类似于IEnumerable.FirstOrDefault 后跟类型转换,它没有您正在寻找的语义。 Seq.pick 类似于 IEnumerable.First 后跟类型转换,这是朝着正确方向迈出的一步(假设您希望在找不到任何匹配项时抛出异常,如 IEnumerable.Single),但是当您实际上不需要类型转换,我认为您真正想要的是Seq.find,它在语义上与IEnumerable.First 相同:

    let UserPlaylists = Request.GetPlaylistsFeed "username"
    let pl = UserPlaylists.Entries |> Seq.find (fun x -> x.Title = "playlistname")
    

    也就是说,如果您真的需要 IEnumerable.Single 而不是 IEnumerable.First 的语义,请忽略这一点并查看 @JoelMueller 的答案。

    【讨论】:

      【解决方案3】:

      使用Option.get方法从选项中取出值

      Seq.tryPick(fun x -> if x.Title="playlistname" then Some(x) else None)
      |> Option.get
      

      尽管在 F# 中,我认为显式处理空案​​例而不是使用异常传播会更惯用

      let value = Seq.tryPick(fun x -> if x.Title="playlistname" then Some(x) else None)
      match value with
      | None ->
        // No matching elements.  Take corrective action
      | Some value ->
        // The value i'm looking for
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-06-07
        • 1970-01-01
        • 2016-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多