При желании взять первый элемент в последовательности - PullRequest
16 голосов
/ 20 сентября 2011

Мне нужна функция, подобная Seq.head, но возвращающая None вместо того, чтобы выдавать исключение, когда последовательность пуста, т.е. seq<'T> -> 'T option.

Есть миллион способов сделать это. Вот несколько:

let items = Seq.init 10 id
let a = Seq.tryFind (fun _ -> true) items
let b = Seq.tryPick Some items
let c = if Seq.isEmpty items then None else Some (Seq.head items)
let d = 
  use e = items.GetEnumerator()
  if e.MoveNext() then Some e.Current
  else None

b это тот, который я использую. Два вопроса:

  1. Есть ли особенно идиоматический способ сделать это?
  2. Поскольку нет встроенной функции Seq.tryHead, означает ли это, что это не должно быть необходимым, является необычным или лучше реализовано без функции?

UPDATE

tryHead был добавлен в стандартную библиотеку в F # 4.0 .

Ответы [ 2 ]

14 голосов
/ 20 сентября 2011

Я думаю, что (б), вероятно, самый идиоматичный, по той же причине, что и @Ramon.

Я думаю, что отсутствие Seq.tryHead просто означает, что это не супер распространено.

Я не уверен, но я предполагаю, что функциональные языки с логическим выводом типов Хиндли-Милнера вообще редко используют такие специфические функции для типов коллекций, потому что перегрузка недоступна, и составление функций более высокого порядка может быть выполнено кратко ,

Например, расширения C # Linq являются гораздо более исчерпывающими, чем функции в модуле F # Seq (который сам по себе является более исчерпывающим, чем функции в конкретных типах коллекций), и даже имеет IEnumerable.FirstOrDefault. Практически каждая перегрузка имеет изменение, которое выполняет map.

Я думаю, что акцент на сопоставление с образцом и конкретные типы, такие как list, также является причиной.

Теперь, большинство из вышеперечисленных является спекуляцией, но я думаю, что у меня может быть понятие, более близкое к объективности. Я думаю, что большую часть времени tryPick и tryFind можно использовать в первую очередь вместо filter |> tryHead. Например, я довольно часто пишу код, подобный следующему:

open System.Reflection
let ty = typeof<System.String> //suppose this type is actually unknown at compile time
seq {
    for name in ["a";"b";"c"] do
        yield ty.GetMethod(name)
} |> Seq.tryFind((<>)null)

вместо

...
seq {
    for name in ["a";"b";"c"] do
        match ty.GetMethod(name) with
        | null -> ()
        | mi -> yield mi
} |> tryHead
1 голос
/ 20 сентября 2011

Вы можете определить:

let seqTryHead s = Seq.tryPick Some s

Это типа seq<'a> -> 'a option.Обратите внимание, что я не бета-уменьшить из-за ограничения общего значения.

...