可以将任何.NET类型/接口模式匹配为列表吗?

时间:2017-09-29 18:37:48

标签: f#

我尝试了匹配.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 个答案:

答案 0 :(得分:3)

F#不像C#那样进行自动类型强制。这意味着您不能比较不同类型的两个值,即使其中一个是另一个的子类型。

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

将此应用于您的特定情况,您无法匹配列表中的非列表值,即使该值在某种程度上是列表的子类型。

但是,如果您真的对这种匹配感兴趣,可以自己编写一个自定义匹配器(也称为“active pattern”),它将采用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?"

答案 1 :(得分:3)

不,用于构造和解构F#列表的语法是专门针对该类型定义的,并且仅适用于该类型(这些运算符实际上用作list的案例标签,从编译器获得特殊处理)。这是核心库中定义列表类型的方式:

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

F#列表与System.Collections.Generic.List根本不同 - 它是cons list,这种性质反映在构造/解构的语法中。

另一方面,

System.Collections.Generic.List是一个可以增大的集合,但是由数组支持并具有数组的性能特征 - 事实上它的F#名称(ResizeArray)是一个更合适的一个。

IList是一个可变列表的接口,而F#list是不可变的 - 它对于它实现IList没有意义。

对于这两种类型 - 以与F#列表相同的方式将它们解构为头部和尾部根本不能反映它们的实际情况,并且将是一种不自然的操作(很多以同样的方式,索引到F#列表是一种不自然的操作。)

答案 2 :(得分:1)

从bcl中,我相信数组是唯一可以匹配的集合类型,而不需要Seq.toList

请参阅https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching#array-pattern

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 [| |] )