2013-06-24 99 views
3

昨天寫了一些代碼時,我遇到了兩個奇怪的問題,這兩個問題都不是我和我的功能性編程導向的朋友都知道的。我們看着它在相當一段時間,並研究它在網絡上,但我們沒能在別處找到任何答案,所以這裏有雲:F#奇數模式匹配問題

的問題是,在此代碼:

天下第一問題:

let outer1 (bs : byte array) = 
    let rec inner (bs : byte array) (bacc : byte array) (i : int) = 
     match i with 
     | bs.Length -> bacc // <--- Error: bs is not recognized. Why? 
     | _ -> bacc.[i] <- bs.[i] 
       inner bs bacc (i + 1) 
    inner bs (Array.zeroCreate bs.Length) 0 

這裏的問題是:FS0039: The namespace or module 'bs' is not defined. 怎麼能這樣呢?畢竟,bs在函數簽名中。此外,在match之前let bsLength = bs.Length定義一個新值。但這樣做我看到一個新的怪胎:

let outer2 (bs : byte array) = 
    let rec inner (bs : byte array) (bacc : byte array) (i : int) = 
     let bsLength = bs.Length 
     match i with 
     | bsLength -> bacc 
     | _ -> bacc.[i] <- bs.[i] // <--- Warning: Rule never matched. Why? 
       inner bs bacc (i + 1) 
    inner bs (Array.zeroCreate bs.Length) 0 

這裏的問題是,上面寫着警告:warning FS0026: This rule will never be matched。 我不明白這一點。 i和陣列的長度沒有關係。如果我寫一個整數(例如10)而不是bsLength,則警告消失。

回答

8

這兩個問題都源於期望模式匹配允許交替使用值和文字。不,不是的。 Pattern Matching (F#) MSDN上的主題很好地概述了支持的模式類型和應用程序的優先規則。簡化冗長描述的主要原則是:除非此值爲文字標識符(區別聯合的案例值,異常標籤或活動模式案例),否則您無法匹配值。

在你的第一個問題點編譯器將bs.Length不是作爲一個屬性數組bsLength像您期望的,但作爲一個文字或不存在的模塊或命名空間bs標識Length;因爲John Palmer指出他的答案,您可以通過使用變量模式與guard statement實現預期的行爲。合法使用匹配表達式類似的圖案的樣品你將是:

module bs = 
    [<Literal>] 
    let Length = 100 
//............................. 
let v = 100; 
let s = match v with 
    | bs.Length -> "matched" 
    | _ -> "not matched";; 

val s : string = "matched" 

第二個問題點是由編譯器處理爲可變圖案,並且bsLength被分配的i而不是被比較的值的值,如你所料;第二個匹配規則沒有機會踢。

+0

謝謝,清除它。我現在更改了代碼,並且它可以工作。此外,現在很高興終於明白問題所在。你的好回答。 :) – oPolo

6

像你認爲它的匹配語句不工作 - 正確的語法是

match i with 
| t when t = bs.Length 

在第二種情況下,實際上創建了一個名爲bsLength新的變量,它隱藏的早期bsLength的定義和匹配所有整數,所以你得到的規則從來沒有匹配的警告。

+0

謝謝,一個簡單但重要的答案。 :) 好答案!清理它,我知道現在的問題,這要歸功於它在那裏創建一個新變量的知識。 :) – oPolo