Может ли какой-либо тип / интерфейс.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 [| |] )