【问题标题】:F# pattern match directly against let bindingF# 模式匹配直接针对 let 绑定
【发布时间】:2014-04-20 21:55:49
【问题描述】:

在 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 变量不同?

【问题讨论】:

  • arg 定义在哪里?
  • @xtofl arg 是在函数中本地定义的,但我相信如果是函数参数也是一样的
  • 我们可以退后一步,问问你想要达到什么目标吗?乍一看,这似乎不是模式匹配的好方法。

标签: f# pattern-matching immutability guard-clause


【解决方案1】:

只需使用大写字母和[<Literal>] 即可按预期工作。

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

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

【讨论】:

  • '按惯例',该惯例是语言的一部分吗?
  • 它是这样的,如果你使用大写标识符,你只会得到一个警告,你不应该在模式匹配中使用大写进行绑定。而如果您使用小写字母,则会将其解释为绑定。
【解决方案2】:

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

假设我有

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

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

回到你遇到的问题:

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

这不起作用的原因是因为在匹配表达式中,F# 将key1 重新绑定到arg 的值,所以key1 -&gt; "value1" is equivalent toif arg1 = arg1 then "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 表达式等同于你的 match 表达式。在这种形式中,很明显第一个条件将始终评估为真。

这里就不放了,看一下匹配表达式的代码引用可能会有所帮助。在看到匹配表达式生成的抽象语法树长什么样子之前,我并没有真正了解匹配表达式发生了什么。

【讨论】:

    【解决方案3】:

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

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

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

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

    有关详细信息,请参阅MSDN article on pattern matching

    【讨论】:

      【解决方案4】:

      您尝试使用的代码引发了两个主要问题。

      首先是模式匹配:

      match arg with
      | _ -> "value"
      

      将 arg 与任何内容匹配,然后返回 "value"

      match arg with
      | a -> "value"
      

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

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

      type Keys=
      | Key1
      | Key2
      

      然后像这样匹配:

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

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

      【讨论】:

        猜你喜欢
        • 2014-08-13
        • 2011-06-28
        • 2015-01-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-06
        • 1970-01-01
        相关资源
        最近更新 更多