我对F#(以及一般的函数式编程)完全不熟悉,但我看到模式匹配在示例代码中随处可见。我想知道模式匹配实际上是如何工作的?例如,我想它在其他语言中与for循环一样工作,并检查集合中每个项目的匹配。这可能远非正确,它在幕后实际上是如何工作的?
答案 0 :(得分:25)
模式匹配实际上如何运作?与
for
循环相同?
它与您可以想象的for
循环相距甚远:而不是循环,模式匹配编译为高效的自动机。自动机有两种风格,我称之为“决策树”和“法式风格”。每种样式都有不同的优点:决策树检查做出决策所需的最小值的数量,但在最坏的情况下,天真的实现可能需要指数代码空间。法国风格提供了不同的时空权衡,两者都有良好但不是最佳的保证。
但是关于这个问题的绝对权威的工作是来自2008 ML研讨会的Luc Maranget的优秀论文"Compiling Pattern Matching to Good Decisions Trees。吕克的论文基本上展示了如何充分利用两个世界。如果你想要一个可以让业余爱好者更容易接受的治疗方法,我谦卑地推荐我自己的产品When Do Match-Compilation Heuristics Matter?
编写模式匹配编译器既简单又有趣!
答案 1 :(得分:17)
这取决于你的意思是什么样的模式匹配 - 它是非常强大的构造,可以以各种方式使用。但是,我将尝试解释模式匹配如何在列表上工作。您可以编写例如这些模式:
match l with
| [1; 2; 3] -> // specific list of 3 elements
| 1::rest -> // list starting with 1 followed by more elements
| x::xs -> // non-empty list with element 'x' followed by a list
| [] -> // empty list (no elements)
F#列表实际上是一个有区别的联合,包含两个案例 - []
代表一个空列表或x::xs
代表一个列表,其中第一个元素x
后跟一些其他元素。在C#中,这可能表示如下:
// Represents any list
abstract class List<T> { }
// Case '[]' representing an empty list
class EmptyList<T> : List<T> { }
// Case 'x::xs' representing list with element followed by other list
class ConsList<T> : List<T> {
public T Value { get; set; }
public List<T> Rest { get; set; }
}
上面的模式将被编译为以下(我使用伪代码使这更简单):
if (l is ConsList) && (l.Value == 1) &&
(l.Rest is ConsList) && (l.Rest.Value == 2) &&
(l.Rest.Rest is ConsList) && (l.Rest.Rest.Value == 3) &&
(l.Rest.Rest.Rest is EmptyList) then
// specific list of 3 elements
else if (l is ConsList) && (l.Value == 1) then
var rest = l.Rest;
// list starting with 1 followed by more elements
else if (l is ConsList) then
var x = l.Value, xs = l.Rest;
// non-empty list with element 'x' followed by a list
else if (l is EmptyList) then
// empty list (no elements)
如您所见,没有涉及循环。在F#中处理列表时,您将使用递归来实现循环,但模式匹配用于组成整个列表的各个元素(ConsList
)。
列表上的模式匹配是 sepp2k 讨论的判别联合的特定情况。模式匹配中可能还有其他构造,但基本上所有构造都是使用一些(复杂的)if
语句编译的。
答案 2 :(得分:3)
不,它不循环。如果你有像这样的模式匹配
match x with
| Foo a b -> a + b
| Bar c -> c
这会编译成类似这样的伪代码:
if (x is a Foo)
let a = (first element of x) in
let b = (second element of x) in
a+b
else if (x is a Bar)
let c = (first element of x) in
c
如果Foo和Bar是来自代数数据类型的构造函数(即定义为type FooBar = Foo int int | Bar int
的类型),则操作x is a Foo
和x is a Bar
是简单的比较。如果它们由active pattern定义,则操作由该模式定义。
答案 3 :(得分:2)
如果您将F#代码编译为.exe,请查看Reflector,您可以看到F#代码的C#“等效”。
我已经用这种方法看了很多F#的例子。