缩短F#元组匹配

时间:2017-02-09 09:24:37

标签: f# pattern-matching

F#beginner here。

我正在尝试将元组与几个案例匹配,并在匹配条件上返回特定值。这是它的外观:

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)

但是编译器说

  

这个'或'模式的两面绑定不同的varibales

所以我尝试了以下内容:

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)

这里我在第二个错误中得到错误从第二个时间条款。他期待一个' - >'或其他令牌。

那么:我如何缩短这种匹配或如何改进它?那些几个Interval(nan,nan)对我来说似乎是不必要的。

提前致谢!

4 个答案:

答案 0 :(得分:5)

您没有正确检查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

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

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

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)

答案 1 :(得分:3)

匹配语句when子句对于之前列出的所有或案例都是通用的。如果你想要想象这个,你可以想到像这样的parens:

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

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

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

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

答案 2 :(得分: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,这意味着您需要在每个间隔检查中以不同方式处理它们。考虑以下两个版本的写作“在一个区间内是一个点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,在您目前返回None的所有情况下都会返回Interval(nan,nan)。否则,您的代码将被全部明确的NaN检查所污染。

答案 3 :(得分:1)

只需在||子句中使用布尔值或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)