5

在 F# 中是否可以直接针对 let 绑定进行模式匹配?

例如,编译时没有任何警告:

    let value = 
        match arg with
        | 1 -> "value1"
        | 2 -> "value2"
        | _ -> failwith "key not found"

而以下针对匹配和的行给出警告“此规则将永远不会被匹配”key2_

    let key1 = 1
    let key2 = 2
    let value = 
        match arg with
        | key1 -> "value1"
        | key2 -> "value2"
        | _ -> failwith "key not found"

这是因为尽管它们是不可变的,但 let 绑定与 C#const变量不同?

4

4 回答 4

6

只需使用大写字母和[<Literal>]它们,它就可以按预期工作。

let [<Literal>] X = 0
let [<Literal>] Y = 1
let bla arg =
    match arg with
    | X -> "zero"
    | Y -> "one"
    | somethingelse -> somethingelse.ToString()

按照惯例,小写名称通常表示绑定到名称的通配符。

于 2014-03-15T15:08:57.607 回答
6

您收到该错误的原因是当您在匹配表达式的模式子句中使用变量名时 F# 正在做什么。

假设我有

match arg with
| x when x = 0 -> "zero"
| y when y = 1 -> "one"
| _ -> "other"

我认为这里的关键是要注意,尽管在比赛之前没有定义 x 或 y,但这段代码仍然可以工作。这是因为 x 和 y 只是使编写匹配表达式更容易的短代码。在幕后,F# 编译器实际上将其x when x = 0转换x为绑定到arg. x然后可以在x = 0表达式和 . 之后的表达式中使用->

回到你遇到的问题:

let key1 = 1
let key2 = 2
let value = 
    match arg with
    | key1 -> "value1"
    | key2 -> "value2"
    | _ -> failwith "key not found"

这不起作用的原因是因为在匹配表达式中,F# 重新绑定key1到 的值arg,所以key1 -> "value1" is equivalent to如果 arg1 = arg1 则为“value1”。第一个模式将始终匹配;所以,key2永远_也达不到。

我不确定我的解释有多清楚,所以我也会用第二种方法来解释发生了什么:

如果将匹配表达式转换为 if-else,它将如下所示:

let key1 = 1
let key2 = 2

let value = 
    if let key1 = arg in arg = key1 then
        "value1"
    else if let key2 = arg in arg = key2 then
        "value2"
    else
        failwith "key not found"

(为什么是的,F# 将允许您将 let 绑定放入 if 表达式中)

此 if/else 表达式等同于您的匹配表达式。在这种形式中,很明显第一个条件将始终评估为真。

这里就不放了,但是看一下匹配表达式的代码引用可能会有所帮助。直到我看到它们生成的抽象语法树是什么样子的,我才真正了解匹配表达式发生了什么。

于 2014-03-15T19:55:49.913 回答
2

如果要匹配模式匹配案例中的特定值,则只能使用文字。一个标识符意味着绑定——即这个模式匹配案例中的实际值将绑定到在这个案例范围内可见的标识符。

正如@DanielFabian 所示,您可以定义自己的文字并为其命名。

如果您需要匹配的值在编译时未知,则可以使用如下守卫:

match arg with
| x when x = key1 -> "value1"
| x when x = key2 -> "value2"
| _ -> // etc

有关更多详细信息,请参阅有关模式匹配的 MSDN 文章

于 2014-03-15T16:42:20.473 回答
1

您尝试使用的代码有两个主要问题。

首先模式匹配:

match arg with
| _ -> "value"

将 arg 与任何内容匹配,然后返回“值”

match arg with
| a -> "value"

将 arg 与任何内容匹配,称其为“a”,然后返回“value”。您可以认为匹配具有自己的小命名空间, a 仅存在于匹配中,而匹配仅“看到”其中已命名的内容。

其次,如果你想匹配一组预定义的值,那么你可能想使用有区别的联合。您可以像这样定义一个:

type Keys=
| Key1
| Key2

然后像这样匹配:

match arg with
| Key1 -> "value1"
| Key2 -> "value2"

在这种情况下,它匹配 Keys 类型,而不是名为 Key1 或 Key2 的值​​。

于 2014-03-17T10:41:10.703 回答