[英]Why can't non-partial active patterns be parameterized in F#?
下面的F#代碼按我的預期工作,打印`匹配為'A':
let (|Char|_|) convf = function
| LazyList.Nil -> None
| LazyList.Cons (x, _) -> Some (convf x)
let test = function
| Char System.Char.ToUpper x -> printfn "Matched as %A" x
| _ -> printfn "Didn't match"
test (LazyList.of_list ['a'])
但是,如果我將Char
從部分活動模式更改為完全活動模式,如下所示:
let (|Char|NoChar|) convf = function
| LazyList.Nil -> NoChar
| LazyList.Cons (x, _) -> Char x
let test = function
| Char System.Char.ToUpper x -> printfn "Matched as %A" x
| NoChar System.Char.ToUpper -> printfn "Didn't match"
test (LazyList.of_list ['a'])
然后,代碼無法編譯,並顯示以下錯誤消息: error FS0191: Only active patterns returning exactly one result may accept arguments.
該示例可能看起來有些虛構,但這是我在業余時間一直在研究的Prolog詞法分析器中嘗試使用的主動模式的簡化版本。 我可以輕松地重寫代碼來避免此問題,但是我很好奇為什么不允許使用這種代碼。
更新: F#的較新版本似乎已重命名此錯誤:
error FS0722: Only active patterns returning exactly one result may accept arguments
注意 這正是Brian所說的,但希望以更清晰的方式表述。
我記得恰好在此問題上記錄了一個錯誤,而IIRC正是Don Syme在此問題上所說的。
多案例活動模式是從某個輸入值到多個輸出值之一的轉換函數。 在您的示例中,任何字符都將轉換為Char大小寫或NoChar大小寫。
這樣做的好處是F#編譯器一次調用多大小寫活動模式函數,然后通常可以確定接下來要評估的模式匹配規則。
但是,如果允許使用參數,則需要為每個模式匹配規則評估多案例活動模式。
所以想像以下
match input with
| Alpha "foo" -> ...
| Bravo "bar" -> ...
評估(| Alpha | Bravo |)“ foo”返回“ Bravo”時,第一個規則將不匹配。 同樣(| Alpha | Bravo |)“ bar”返回'Alpha',則第二個規則也不匹配。 因此,您實際上並沒有多案例活動模式。 只是參數化的部分活動模式。 (由於某些輸入,不會遇到預期的模式情況。)
因此,當遇到語言的一角並沒有多大意義時,實際上可以通過部分參數化的活動模式使它更加清晰。 該功能未添加到語言中。
我不能肯定地說(不知道實際的設計原理),但是嘗試對其進行反向工程時,您希望這段代碼做什么?
let (|Char|NoChar|) pred = function
| LazyList.Nil -> NoChar
| LazyList.Cons (x, _) -> if pred x then Char x else NoChar
let test = function
| Char System.Char.IsLetter x -> printfn "Matched as %A" x
| NoChar System.Char.IsDigit -> printfn "Didn't match"
test (LazyList.of_list ['a'])
test (LazyList.of_list ['1'])
考慮到非局部活動模式應該划分整個空間,如果在同一個匹配項中給每個參數一個不同的參數,那將很奇怪,因為那樣的話,它們可能“失敗”或“都成功”。 (這也暗示了它們的實現方式,例如,就像在進行比賽之前捕獲其自變量的模式一樣。捕獲的自變量在比賽的所有分支之間都是不變的。)
這也表明你可以寫例如
let test convf l =
let (|Char|NoChar|) = function
| LazyList.Nil -> NoChar
| LazyList.Cons (x, _) -> Char(convf x)
match l with
| Char x -> printfn "Matched as %A" x
| NoChar -> printfn "Didn't match"
test System.Char.ToUpper (LazyList.of_list ['a'])
(盡管我不知道這對您的特定應用程序是否方便/現實)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.