Может ли какой-либо тип / интерфейс.NET соответствовать шаблону в виде списка?

Я пробовал сопоставление с образцом.NET списков типов (например, IList<> а также List<>):

match myIList with
| [] -> ... 

Я получил сообщение об ошибке The expression was expected to have type IList<...> but here has type 'a list, Я понимаю, почему это не работает - очевидно, типы не совместимы (я бы предположил, что тип списка F# не реализует IList<>).

Что мне интересно, есть ли какие-либо типы списков BCL, с которыми можно сопоставить шаблон, не добавляя Seq.toList в match выражение?

3 ответа

Решение

Нет, синтаксис, используемый для создания и деконструкции списка F#, определен специально для этого типа и только для этого типа (эти операторы фактически используются в качестве меток для list которые получают специальную обработку от компилятора). Вот как тип списка определяется в основной библиотеке:

type List<'T> = 
   | ([])  :                  'T list
   | (::)  : Head: 'T * Tail: 'T list -> 'T list

F# список принципиально отличается от System.Collections.Generic.List - это список минусов, и эта природа отражена в синтаксисе для строительства / деконструкции.

System.Collections.Generic.List с другой стороны, это коллекция, которая может увеличиваться в размерах, но поддерживается массивом и имеет характеристики производительности массива - фактически это имя F# (ResizeArray) является гораздо более подходящим.

IList является интерфейсом для изменяемого списка, тогда как список F# является неизменным - для него не имеет смысла реализовывать IList.

Для обоих этих типов - деконструкция их в голову и хвост таким же образом, как это делается для списков F#, просто не отражает реальность того, чем они являются, и была бы неестественной операцией (во многом аналогичной индексации в F#). список это неестественная операция).

F# не выполняет автоматическое приведение типов, как в C#. Это означает, что вы не можете, например, сравнить два значения разных типов, даже если одно из них является подтипом другого.

let x : string = "abc"
let y : obj = box "abc"
let eq = x = y  // Type mismatch error here

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

Однако, если вы действительно заинтересованы в таком сопоставлении, вы можете написать собственный сопоставитель (также известный как " активный шаблон"), который будет seq<'t> и сопоставьте его с обычными списками F#:

let (|IsList|) l = Seq.toList l

Такой сопоставитель пригоден для использования со всем, что может быть seq - даже, например, строка:

match "abcd" with
| IsList [] -> "Empty string"
| IsList ('a'::_) -> "Starts with an a"
| IsList _ -> "Something else"

Работает и для обычных списков:

match [1,2,3] with
| IsList [1,2,3] -> "One, two, three"
| IsList [] -> "Empty list"
| IsList _ -> "Huh?"

С точки зрения bcl, я считаю, что массивы - это единственный тип коллекции, который может быть сопоставлен без необходимости Seq.toList

См. https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching

let vectorLength vec =
    match vec with
    | [| var1 |] -> var1
    | [| var1; var2 |] -> sqrt (var1*var1 + var2*var2)
    | [| var1; var2; var3 |] -> sqrt (var1*var1 + var2*var2 + var3*var3)
    | _ -> failwith "vectorLength called with an unsupported array size of %d." (vec.Length)

printfn "%f" (vectorLength [| 1. |])
printfn "%f" (vectorLength [| 1.; 1. |])
printfn "%f" (vectorLength [| 1.; 1.; 1.; |])
printfn "%f" (vectorLength [| |] )
Другие вопросы по тегам