Использование неполного сопоставления с образцом в качестве фильтра?

Предположим, у меня есть следующий код:

type Vehicle =
| Car  of string * int
| Bike of string

let xs = [ Car("family", 8); Bike("racing"); Car("sports", 2); Bike("chopper") ]

Я могу отфильтровать список выше, используя неполное сопоставление с шаблоном в обязательном порядке для цикла вроде:

> for Car(kind, _) in xs do
>    printfn "found %s" kind;;

found family
found sports
val it : unit = ()

но это вызовет:warning FS0025: Incomplete pattern matches on this expression. For example, the value 'Bike (_)' may indicate a case not covered by the pattern(s). Unmatched elements will be ignored.

Поскольку мое намерение заключается в игнорировании непревзойденных элементов, есть ли возможность избавиться от этого предупреждения?

И есть ли способ заставить эту работу работать со списками, не вызывая исключение MatchFailureException? например что-то подобное:

> [for Car(_, seats) in xs -> seats] |> List.sum;;
val it : int = 10

3 ответа

Решение

Два года назад ваш код был действительным, и это был стандартный способ сделать это. Затем язык был очищен, и проектное решение было в пользу явного синтаксиса. По этой причине я думаю, что не стоит игнорировать предупреждение.

Стандартная замена для вашего кода:

for x in xs do
    match x with
    | Car(kind, _) -> printfn "found %s" kind
    | _ -> ()

(Вы также можете использовать функции высокого порядка в образце площадки)

С другой стороны, List.sumBy вполне подойдет:

xs |> List.sumBy (function Car(_, seats) -> seats | _ -> 0)

Если вы предпочитаете придерживаться понимания, это явный синтаксис:

[for x in xs do
    match x with
    | Car(_, seats) -> yield seats
    | _ -> ()
] |> List.sum

Вы можете отключить любое предупреждение через #nowarn директива или --nowarn: опция компилятора (передайте номер предупреждения, здесь 25 как в FS0025).

Но в целом нет, лучше всего явно фильтровать, как в другом ответе (например, с choose).

Чтобы явно указать, что вы хотите игнорировать несопоставленные случаи, вы можете использовать List.choose и вернуться None для тех непревзойденных элементов. Ваши коды могут быть написаны более логичным образом следующим образом:

let _ = xs |> List.choose (function | Car(kind, _) -> Some kind
                                    | _ -> None)
           |> List.iter (printfn "found %s")

let sum = xs |> List.choose (function | Car(_, seats)-> Some seats
                                      | _ -> None) 
             |> List.sum
Другие вопросы по тегам