2017-04-19 60 views
2

是否有可能像使用F#中的列表一样使用匹配匹配来遍历数组?我试过这样的事情:数组模式匹配

type Alphabet = A | B 

let rec calc (l: Alphabet []) = match l with 
           |l when l.[0] = A -> 5+(calc l.[1..]) 
           |l when l.[0] = B -> 10+(calc l.[1..]) 
           |l when l = [||] -> 0 
calc [|A;A;B|] 

问题似乎是循环继续,并产生一个stackoverflow。是否有可能这样做?

回答

7

我认为你正在试图做到这一点:

let toInteger = function 
    | A -> 5 
    | B -> 10 

[|A; A; B|] |> Array.sumBy toInteger 

在模式匹配,你可以使用数组模式:例如,对一个三元素的数组[|a; b; c|]匹配。但是对于数组,没有::运算符,因此使用列表的方式使用数组很麻烦。这是因为您不能复制数组的尾部而将其作为新数组。

有一些与问题的代码问题:

  • 它崩溃,由于外出数组的边界。这是因为您没有验证.[1..]切片存在。
  • 它不是尾递归。这可能是你在长列表上看到堆栈溢出的原因。
  • 将两个功能混合到一个功能中,使其阅读变得复杂:转换为整数和求和。
  • 编译器无法验证模式匹配是否涵盖所有情况,因此它会发出警告。
  • 正如之前所暗示的,数组的复制是昂贵的。这个算法在数组长度上有O(n^2),这使得它对于长阵来说非常昂贵。不像Array.sumBy,或者是一个尾部递归函数索引到数组中,根本不需要复制数组。

问题的核心在于数组和单链不可变列表之间的区别。数组适合通过增加一个索引(Array.sumBy内部执行)来迭代它们,而列表允许将它们的尾部作为独立列表对待,而不需要复制(其中List.sumBy does internally)。通常最好按照打算使用的方式使用每个数据结构。