对于笛卡尔生产,有一个足够好的功能 -定义如下的序列:
let rec sequence = function
| [] -> Seq.singleton []
| (l::ls) -> seq { for x in l do for xs in sequence ls do yield (x::xs) }
但看看它的结果:
序列 [[1..2];[1..10000]] |> Seq.skip 1000 ;; 验证它:seq = seq [[1; 1001];[1; 1002];[1; 1003];[1; 1004];...]
我们可以看到产品的第一个“坐标”变化非常缓慢,当第二个列表结束时它会改变值。
我编写了自己的序列如下(评论如下):
/// Sum of all producted indeces = n
let rec hyper'plane'indices indexsum maxlengths =
match maxlengths with
| [x] -> if indexsum < x then [[indexsum]] else []
| (i::is) -> [for x in [0 .. min indexsum (i-1)] do for xs in hyper'plane'indices (indexsum-x) is do yield (x::xs)]
| [] -> [[]]
let finite'sequence = function
| [] -> Seq.singleton []
| ns ->
let ars = [ for n in ns -> Seq.toArray n ]
let length'list = List.map Array.length ars
let nmax = List.max length'list
seq {
for n in [0 .. nmax] do
for ixs in hyper'plane'indices n length'list do
yield (List.map2 (fun (a:'a[]) i -> a.[i]) ars ixs)
}
关键思想是将(两个)列表视为(两个)正交维度,其中每个元素都由列表中的索引标记。因此,我们可以通过超平面枚举笛卡尔积的每个部分中的每个元素来枚举所有元素(在二维情况下,这是一条线)。换句话说,想象一下 excel 的工作表,其中第一列包含从 [1;1] 到 [1;10000] 的值,第二列包含从 [2;1] 到 [2;10000] 的值。编号为 1 的“超平面”是连接单元格 A2 和单元格 B1 的线。对于我们的例子
超'平面'索引 0 [2;10000];; 验证它:int list list = [[0; 0]]
超'平面'索引 1 [2;10000];; 验证它:int list list = [[0; 1]; [1; 0]]
超'平面'索引 2 [2;10000];; 验证它:int list list = [[0; 2]; [1; 1]]
超'平面'索引 3 [2;10000];; 验证它:int list list = [[0; 3]; [1; 2]]
超'平面'索引 4 [2;10000];; 验证它:int list list = [[0; 4]; [1; 3]]
好吧,如果我们有从给定列表生成的索引和数组,那么我们现在可以将序列定义为{平面 0 中的所有元素;比平面 1 中的所有元素 ... 等等 } 并获得比原始序列更多的 volatile 函数。
但是finite'sequence却变成了非常贪吃的函数。现在的问题。我怎样才能改进它?
带着最良好的祝愿,亚历山大。(抱歉英语不好)