2017-10-09 96 views
2

我有以下代碼(`IGNORECASE是我在別處定義的有源圖案):負模式匹配

match myType with 
| {Field1 = IgnoreCase "invalid"} -> None 
| {Field2 = Some f2 
    Field3 = Some f3 
    Field4 = None | Some (0 | 1 | 2)} 
    -> Some (f2, f3) 
| _ -> None 

正如你可以看到,Field1具有黑名單值和Field4已列入白名單的值(這樣做Field2Field3,因爲它們必須是Some)。恕我直言,如果我可以在同一種情況下進行所有檢查,例如Field1 = IgnoreCase "invalid"與其他比賽一起使用,恕我直言,它會看起來稍微更清潔。 Field1 <> IgnoreCase "invalid",但該特定示例不能編譯。我瞭解守衛,但看起來不像原來的解決方案。

是否有可能做「負面」(邏輯NOT)模式匹配的意義上說,一個值不應該匹配其他值,而不使用守衛?

+1

當f <>「invalid」時,您可以添加一個警衛:'{Field1 = f}。 – Lee

+0

謝謝。我瞭解守衛,並打算在問題中提到這一點。現在更新。 – cmeeren

回答

3

據我所知,有做到這一點沒有什麼好辦法。您可以看到支持的模式here並驗證沒有否定結構。缺乏這種功能的一個原因可能是它會與其他模式匹配功能奇怪地相互作用(例如,在負面語境內引入標識符與模式的含義是什麼?)。這不是你可以通過活動模式以一般方式實現的東西,因爲活動模式接收到值而不是模式表達式。

1

我會用積極的方式這樣

let (|Check|_|) x = 
    let = { Field2 = f2; Field3 = f3; Field4 = f4 } 
    let check = 
     f4 = None || 
     f4 = Some 1 || 
     f4 = Some 2 || 
     f4 = Some 3 
    if check then Some (f2, f3) else None 

    match myType with 
    | {Field1 = "invalid"} -> None 
    | Check (x, y) -> Some (x, y) 
    | _ -> None 
2

你可以定義一個名爲Not有源圖案:

let (|Not|_|) a b = if a <> b then Some() else None 

然後你可以使用它的模式匹配內:

match myType with 
| {Field1 = Not "invalid" 
    Field2 = Some f2 
    Field3 = Some f3 
    Field4 = None | Some (0 | 1 | 2)} 
    -> Some (f2, f3) 
| _ -> None 

此活動模式的侷限性在於它必須採用文字值,而不是子模式。活動模式不像內置模式那樣可組合。然而,這也是一個潛在的優勢,因爲你可以把它作爲標識符值而不是一個新的值綁定到標識符:

let invalidString = "invalid" 

match myType with 
| {Field1 = Not invalidString 
... 
+0

謝謝。我實際上是在發佈之前嘗試過的,正如你所說,它不適用於子模式。不幸的是,在我的情況下需要子模式,但我不認爲將其包含在問題中。我已經更新了這個問題來澄清這一點。 – cmeeren

+0

@cmeeren你可以有另一個活動模式:'let(| ToLower |)x =(x:string).ToLower()'然後執行'ToLower(不是「無效)',但現在變得愚蠢。 。我認爲你在模式匹配方面做得太多了。使用警衛或一個專門的活動模式:) – TheQuickBrownFox