簡體   English   中英

為什么不能在F#中對非局部活動模式進行參數化?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM