Доступ к элементам в IEnumerable в F#

#c# #f# #youtube

#c# #f# #YouTube

Вопрос:

Я пытаюсь использовать Youtube .Net API в F # и я столкнулся с проблемой, пытаясь получить доступ к IEnumerable, возвращенному в userPlaylists.Свойство записей. Ниже приведен код c #, который я тестировал, однако, похоже, я не могу вернуть отдельные элементы из коллекции IEnumerable в f #.

  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?

Ответ №1:

Метод расширения Single() выдает исключение, если не находит совпадения, и он также выдает исключение, если находит более одного совпадения. В модуле F # Seq нет прямого эквивалента. Самым близким было бы то, Seq.find что like .Single() выдаст исключение, если не найдет совпадения, однако в отличие от .Single() этого, он прекратит поиск, как только найдет совпадение, и не выдаст ошибку, если существует более одного совпадения.

Если вам действительно нужно поведение «выдавать ошибку при более чем одном совпадении», то проще всего было бы использовать именно этот метод из F#:

 open System.Linq

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

Если вам не нужно поведение «throw if more than one match», то вам также не следует использовать .Single() в вашем коде C # — .First() будет работать лучше, потому что он прекращает поиск, как только находит совпадение.

На моем месте я бы для краткости использовал use Seq.tryFind over 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