Матч с пустой последовательностью

Я изучаю F#, и я начал играть с обеими последовательностями и match выражения.

Я пишу веб-скребок, который просматривает HTML, похожий на следующий, и беру последний URL в родительском. <span> с paging учебный класс.

<html>
<body>
    <span class="paging">
        <a href="http://google.com">Link to Google</a>
        <a href="http://TheLinkIWant.com">The Link I want</a>
    </span>
</body>
</html>

Моя попытка получить последний URL выглядит следующим образом:

type AnHtmlPage = FSharp.Data.HtmlProvider<"http://somesite.com">

let findMaxPageNumber (page:AnHtmlPage)= 
    page.Html.Descendants()
    |> Seq.filter(fun n -> n.HasClass("paging"))
    |> Seq.collect(fun n -> n.Descendants() |> Seq.filter(fun m -> m.HasName("a")))
    |> Seq.last
    |> fun n -> n.AttributeValue("href")

Однако я сталкиваюсь с проблемами, когда искомый класс отсутствует на странице. В частности, я получаю ArgumentExceptions с сообщением: Additional information: The input sequence was empty.

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

let findUrlOrReturnEmptyString (span:seq<HtmlNode>) =
    match span with 
    | Seq.empty -> String.Empty      // <----- This is invalid
    | span -> span
    |> Seq.collect(fun (n:HtmlNode) -> n.Descendants() |> Seq.filter(fun m -> m.HasName("a")))
    |> Seq.last
    |> fun n -> n.AttributeValue("href")

let findMaxPageNumber (page:AnHtmlPage)= 
    page.Html.Descendants()
    |> Seq.filter(fun n -> n.HasClass("paging"))
    |> findUrlOrReturnEmptyStrin

Моя проблема сейчас Seq.Empty не является литералом и не может использоваться в шаблоне. Большинство примеров с сопоставлением с образцом указывают пустые списки [] в их шаблонах, поэтому мне интересно: как я могу использовать аналогичный подход и сопоставить пустые последовательности?

3 ответа

Решение

Предложение, которое ildjarn дал в комментариях, является хорошим: если вы чувствуете, что используя match создаст более читаемый код, а затем создаст активный шаблон для проверки пустых последовательностей:

let (|EmptySeq|_|) a = if Seq.isEmpty a then Some () else None

let s0 = Seq.empty<int>

match s0 with
| EmptySeq -> "empty"
| _ -> "not empty"

Запустите это в F# интерактивно, и результат будет "empty",

Вы можете использовать when Охранник для дальнейшей квалификации дела:

match span with 
| sequence when Seq.isEmpty sequence -> String.Empty
| span -> span
|> Seq.collect(fun (n:HtmlNode) -> n.Descendants() |> Seq.filter(fun m -> m.HasName("a")))
|> Seq.last
|> fun n -> n.AttributeValue("href")

ildjarn прав в том, что в этом случае if...then...else может быть более читаемой альтернативой, хотя.

Используйте пункт охраны

match myseq with
| s when Seq.isEmpty s -> "empty"
| _ -> "not empty"

Основываясь на ответе от @rmunn, вы можете создать более общую модель равенства последовательностей.

let (|Seq|_|) test input =
    if Seq.compareWith Operators.compare input test = 0
        then Some ()
        else None

match [] with
| Seq [] -> "empty"
| _ -> "not empty"
Другие вопросы по тегам