【问题标题】:Array pattern matching数组模式匹配
【发布时间】:2017-04-19 10:03:46
【问题描述】:
是否可以像 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|]
问题似乎是循环继续并导致堆栈溢出。有可能吗?
【问题讨论】:
标签:
f#
functional-programming
【解决方案1】:
我认为您正在尝试这样做:
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)。通常最好按照预期的方式使用每个数据结构。