参数类型的模式匹配如何在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
类型,但猜测可能有更好的解决方案 - 例如检查输入是否为数字(一般情况下)如果是这样,把它扔到浮动?
答案 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,那么你 应该使用相同的技术。