F#按类型匹配

时间:2013-05-21 10:45:32

标签: f# pattern-matching

参数类型的模式匹配如何在F#中起作用?

例如,我正在尝试编写一个简单的程序,如果提供了 number ,它将计算平方根,否则返回它的参数。

open System

let my_sqrt x =
  match x with
  | :? float as f -> sqrt f
  | _ -> x


printfn "Enter x"
let x = Console.ReadLine()

printfn "For x = %A result is %A" x (my_sqrt x)

Console.ReadLine()

我收到此错误:

error FS0008: This runtime coercion or type test from type
    'a    
 to 
    float    
involves an indeterminate type based on information prior 
to this program point. Runtime type tests are not allowed
on some types. Further type annotations are needed.

由于sqrt适用于float,我会检查float类型,但猜测可能有更好的解决方案 - 例如检查输入是否为数字(一般情况下)如果是这样,把它扔到浮动?

2 个答案:

答案 0 :(得分:5)

此处的问题是x的类型实际上是string。添加它来自Console.ReadLine,该字符串中存储的信息类型只能在运行时确定。这意味着您既不能使用模式匹配,也不能使用强制模式匹配。

但您可以使用Active Patterns。由于x中存储的实际数据仅在运行时已知,因此您必须解析字符串并查看包含的内容。

假设您期待float,但您无法确定,因为用户可以输入他们想要的任何内容。我们将尝试解析我们的字符串:

let my_sqrt x =
    let success, v = System.Single.TryParse x // the float in F# is represented by System.Single in .NET
    if success then sqrt v
    else x

但这不会编译:

  

此表达式应该具有float32类型,但这里有类型字符串

问题是编译器根据表达式float32推断函数返回sqrt (System.Single.Parse(x))。但是如果x没有解析浮动,我们打算只返回它,而x是一个字符串,我们在这里有一个不一致。

要解决此问题,我们必须将sqrt的结果转换为字符串:

let my_sqrt x =
    let success, v = System.Single.TryParse x
    if success then (sqrt v).ToString()
    else x

好的,这应该可行,但它不使用模式匹配。所以让我们定义我们的“活动”模式,因为我们不能在这里使用常规模式匹配:

let (|Float|_|) input =
    match System.Single.TryParse input with
    | true, v -> Some v
    | _ -> None

基本上,只有input可以正确解析为浮点文字时,此模式才会匹配。以下是它在初始函数实现中的使用方法:

let my_sqrt' x =
    match x with
    | Float f -> (sqrt f).ToString()
    | _ -> x

这看起来很像你的功能,但请注意我仍然需要添加.ToString()位。

希望这有帮助。

答案 1 :(得分:2)

引用唯一的Scott Wlaschin 'F# for fun and profit' site

  

匹配子类型您可以使用以下符号匹配子类型:?运营商,   这给你一个粗略的多态性:

let x = new Object()
let y = 
    match x with 
    | :? System.Int32 -> 
        printfn "matched an int"
    | :? System.DateTime -> 
        printfn "matched a datetime"
    | _ -> 
        printfn "another type"
     

这只能找到父类的子类(在这种情况下,   宾语)。表达式的整体类型具有父类   输入

     

请注意,在某些情况下,您可能需要“装箱”该值。

let detectType v =
    match v with
        | :? int -> printfn "this is an int"
        | _ -> printfn "something else"
// error FS0008: This runtime coercion or type test from type 'a to int  
// involves an indeterminate type based on information prior to this program point. 
// Runtime type tests are not allowed on some types. Further type annotations are needed.
     

该消息告诉您问题:“不允许运行时类型测试   在某些类型“。答案是“强制”强制它进入的值   引用类型,然后您可以键入check it:

let detectTypeBoxed v =
    match box v with      // used "box v" 
        | :? int -> printfn "this is an int"
        | _ -> printfn "something else"

//test
detectTypeBoxed 1
detectTypeBoxed 3.14
     

在我看来,匹配和调度类型只是代码气味   因为它是面向对象的编程。偶尔有必要,   但粗心使用表明设计不佳。

     

在良好的面向对象设计中,正确的方法是使用   用于替换子类型测试的多态性以及此类技术   作为双重派遣。所以如果你在F#中做这种OO,那么你   应该使用相同的技术。