2017-02-09 126 views
2

F#初學者在這裏。縮短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) 

由於幾乎所有情況下返回間隔(男,男)我想將它們分組,由於可讀性,但我不知道怎麼樣。我嘗試以下

​​

但是編譯器說

的這種雙方「或」模式結合不同的組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)似乎對我來說不太合適。

在此先感謝!

+3

我懷疑'| | nan,_ | _,nan'就是你所希望的...... – ildjarn

+0

https://en.wikipedia.org/wiki/NaN – FuleSnabel

回答

3

在匹配語句when子句對於以前列出的所有或案例很常見。如果你要想象這一點,你能想到像有小括號這樣的:

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

不能重複when第幾次你有結果表達式(->之後)。

相反,你可以參加所有的條件在一個when條款,類似於:

| _ when cond1 || 
     cond2 || 
     cond3 -> ... 
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) 
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 

你應該使用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) 
+0

Upvote指出NaN檢查不能像例外那樣工作 - 但是,你的解決方案不會匹配原始問題的語義。發佈一個新的答案。 –

+0

@AntonSchwaighofer你是對的。我修改了我的答案。 – TheInnerLight

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檢查丟棄。