2011-05-17 116 views
3

我試圖在F#中使用Youtube .Net API,並遇到嘗試訪問返回給userPlaylists.Entries屬性的IEnumerable的問題。下面是我測試過的c#代碼,但是我似乎無法從f#中的IEnumerable集合中返回單個項目。在F中的IEnumerable中訪問項目#

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

條目解析爲IEnumerable<Playlist> Feed<PlayList>.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檢索單個項目的正確方法?

+1

你怎麼想如果在seq中找不到所需的項目,請執行操作嗎? – Brian 2011-05-17 23:10:43

回答

2

的空情況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 

我愛的管道運營商...

+0

這個伎倆,謝謝。 – Dale 2011-05-18 21:05:05

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的答案。

1

使用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