bca*_*cat 13 .net syntax f# design-patterns
以下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'])
Run Code Online (Sandbox Code Playgroud)
但是,如果我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'])
Run Code Online (Sandbox Code Playgroud)
然后代码无法编译,给出以下错误消息: 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
Chr*_*ith 13
NB.这正是Brian所说的,但希望以更清晰的方式说明.
我记得正是在这个问题上记录了一个错误,而IIRC这就是Don Syme对此事所说的话.
多案例活动模式是从某个输入值到几个输出值之一的转换函数.在您的示例中,任何字符都将转换为Char案例或NoChar案例.
这样做的好处是F#编译器调用一次多案例活动模式函数,然后通常可以确定下一个要评估的模式匹配规则.
但是,如果允许参数,则需要为每个模式匹配规则评估多案例活动模式.
想象一下以下内容
match input with
| Alpha "foo" -> ...
| Bravo "bar" -> ...
Run Code Online (Sandbox Code Playgroud)
当评估(| Alpha | Bravo |)"foo"返回'Bravo'时,第一个规则将不匹配.Likeways(| 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'])
Run Code Online (Sandbox Code Playgroud)
鉴于非部分活动模式应该划分整个空间,如果你在同一个匹配中给每个不同的参数会很奇怪,因为那样它们可能“都失败”或“都成功”。(这也暗示了它们如何实现,例如,就像在进行匹配之前捕获其参数的模式一样。捕获的参数在匹配的所有分支中都是不变的。)
它还建议您可以编写例如
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'])
Run Code Online (Sandbox Code Playgroud)
(虽然我不知道这对于您的特定应用程序是否方便/现实)。