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