繁体   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