【问题标题】:Shorten F# Tuple Matching缩短 F# 元组匹配
【发布时间】:2017-02-09 09:24:37
【问题描述】:

这里是 F# 初学者。

我正在尝试将一个元组与多个案例进行匹配,并在匹配条件下返回一个特定值。看起来是这样的:

match inf, sup with
            | nan , _  
            | _ , nan  
                -> Interval(nan,nan) 

            | _ , _  when inf = -infinity && sup = -infinity -> Interval(nan,nan) 
            | _ , _  when inf = infinity && sup = infinity -> Interval(nan,nan)

            | _ , _  when inf > sup -> Interval(nan,nan)         

            | _ , _ -> Interval(inf,sup)

由于几乎每个案例都返回 Interval(nan,nan),我想将它们分组,因为可读性好,但我不知道如何。我尝试了以下

match inf, sup with
            | nan , _  
            | _ , nan  
            | _ , _  when inf = -infinity && sup = -infinity
            | _ , _  when inf = infinity && sup = infinity
            | _ , _  when inf > sup -> Interval(nan,nan) 
                -> Interval(nan,nan)

            | _ , _ -> Interval(inf,sup)

但是编译器说

这种“或”模式的两侧绑定不同的变量集

所以我尝试了以下方法:

match inf, sup with
            | nan , _  
            | _ , nan  
                -> Interval(nan,nan)
            | _ , _  when inf = -infinity && sup = -infinity
            | _ , _  when inf = infinity && sup = infinity
            | _ , _  when inf > sup -> Interval(nan,nan) 
                -> Interval(nan,nan)

            | _ , _ -> Interval(inf,sup)

这里我在第二个得到一个错误 |从第二个 when 子句开始。他需要一个“->”或其他标记。

那么:我怎样才能缩短这个匹配或怎样改进它?这几个 Interval(nan,nan) 对我来说似乎并不重要。

提前致谢!

【问题讨论】:

标签: f# pattern-matching


【解决方案1】:

您没有正确检查nan。匹配nan 将导致任何提供的值绑定到一个名为nan 的值。 FSI 通知:

let isNan x = match x with |nan -> true |_ -> false;;

let isNan x = match x with |nan -> true |_ -> false;;
-----------------------------------------^

stdin(1,42): warning FS0026: This rule will never be matched

您应该使用System.Double.IsNaN(x) 检查nan 的值。

考虑到这一点,我将使用主动模式来检查无效值:

let (|PositiveInfinity|NegativeInfinity|NaN|Real|) = function
    |v when v = -infinity -> NegativeInfinity
    |v when v = infinity -> PositiveInfinity
    |v when System.Double.IsNaN(v) -> NaN
    |v -> Real v

然后将您的模式匹配减少到:

let test inf sup =
    match inf, sup with
    |NaN, _
    |_, NaN
    |NegativeInfinity, NegativeInfinity
    |PositiveInfinity, PositiveInfinity
    |_, _ when inf > sup -> Interval(nan, nan)
    |_, _  -> Interval(inf, sup)

【讨论】:

  • 赞成指出 NaN 检查无法正常工作 - 但是,您的解决方案与原始问题的语义不匹配。发布新答案。
  • @AntonSchwaighofer 你是对的。我修改了答案。
【解决方案2】:

在匹配语句中when 子句对于所有或前面列出的情况都是通用的。如果您想将其可视化,您可以认为有这样的括号:

( | Case1 n
  | Case2 n
  | Case3 n ) when predicate(n) ->

在得到结果表达式之前,您不能多次重复when 子句(在-> 之后)。

相反,您可以在一个 when 子句中加入所有条件,类似于:

| _ when cond1 ||
         cond2 ||
         cond3 -> ...

【讨论】:

    【解决方案3】:

    @TheInnerLight 给出的答案很棒,但处理极端情况的方式不同。 OP 认为 (-inf, 10.0) 之间的间隔是有效的,但发布的答案不是。这是一个处理所有情况的答案:

    let (|PositiveInfinity|NegativeInfinity|NaN|Real|) = function
        | v when Double.IsPositiveInfinity v -> PositiveInfinity
        | v when Double.IsNegativeInfinity v -> NegativeInfinity
        | v when Double.IsNaN v -> NaN
        | v -> Real v
    
    let getInterval inf sup =
        match inf, sup with
        | NaN, _ 
        | _ , NaN
        | NegativeInfinity, NegativeInfinity
        | PositiveInfinity, PositiveInfinity 
        | _ , _  when inf > sup -> Interval(nan,nan) 
        | _ , _ -> Interval(inf,sup)
    

    稍微偏离主题:我通常建议不要使用NaN 作为无效间隔的标记。与NaN 的任何比较都返回false,这意味着您需要在每次间隔检查中以不同方式处理它们。考虑以下 2 个版本的写作“区间内的点 v?”,其中下限和上限恰好是 NaN

    let inside1 v = v >= nan && v <= nan
    let inside2 v = not (v < nan || v > nan)    
    

    inside1,你检查“点在里面吗?”,在inside2,你问“它不在外面吗?”。检查点 1.0 给出:

    > inside1 1.0;;
    val it : bool = false
    > inside2 1.0;;
    val it : bool = true
    

    因此,我建议您的函数返回一个Interval option,这意味着在您目前返回Interval(nan,nan) 的所有情况下None。否则,您的代码将被明确的NaN 检查弄得乱七八糟。

    【讨论】:

      【解决方案4】:

      只需在 when 子句中使用布尔值或 ||

      match inf, sup with
              | nan , _  
              | _ , nan  
                  -> Interval(nan,nan) 
              | _ , _  when inf = -infinity && sup = -infinity ||
                            inf = infinity  && sup = infinity ||
                            inf > sup -> Interval(nan,nan)       
              | _ , _ -> Interval(inf,sup)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-09-25
        • 1970-01-01
        • 1970-01-01
        • 2012-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多