模式匹配如何在F#幕后工作?

时间:2010-05-25 20:39:04

标签: algorithm f# functional-programming pattern-matching

我对F#(以及一般的函数式编程)完全不熟悉,但我看到模式匹配在示例代码中随处可见。我想知道模式匹配实际上是如何工作的?例如,我想它在其他语言中与for循环一样工作,并检查集合中每个项目的匹配。这可能远非正确,它在幕后实际上是如何工作的?

4 个答案:

答案 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 Foox is a Bar是简单的比较。如果它们由active pattern定义,则操作由该模式定义。

答案 3 :(得分:2)

如果您将F#代码编译为.exe,请查看Reflector,您可以看到F#代码的C#“等效”。

我已经用这种方法看了很多F#的例子。