我已经为此苦了一段时间了。虽然我有一些行之有效的命令式方法,但我还是决定重新设计此部分:
列出每个子列表的第一项,然后再列出第一项,但是从最后一个子列表开始,第二项,然后是第三项,直到最后一个列表用尽,对N-做相同的操作1个子列表,基本上给出了所有这些列表的一种产物
换句话说:[abc][FG][98]
的计算方式应类似于(将函数f
应用于每个项目,逗号用于可读性):[aF9,aF8,aG9,aG8,bF9,bF8,bG9,bG8 ,cF9,cF8,cG9,cG8]
平整化结果并将其应用于结果的每个项目,结果本身将返回列表
0 .. E-1
(在我的示例中不是)这是一个工作示例,清楚地表明了其递归性质,但显然仅深入到了三个列表:
let rec nestedApply f inp =
match inp with
| [] -> []
| [x] -> x |> List.collect f
| [x;y] ->
x
|> List.collect (fun a -> y |> List.collect (fun b -> [a;b]))
|> List.collect f
| [x;y;z] ->
x
|> List.collect (fun a -> y |> List.collect (fun b -> z |> List.collect (fun c -> [a;b;c])))
|> List.collect f
| head::tail ->
// ??? I gave the following several attempts but couldn't get it right
nestedApply f tail
|> List.collect (fun a -> a::head)
|> List.collect f
我更喜欢不会炸毁堆栈的解决方案。最终,我需要将其用于延迟评估,因此我可能会求助于序列,但是对于列表,我认为该算法将是最容易考虑的。
示例:nestedApply (fun a -> [a]) [[1 .. 5];[6;7];[11;12]]
示例输出:
[1; 6; 11; 1; 6; 12; 1; 7; 11; 1; 7; 12; 2; 6; 11; 2; 6; 12; 2; 7; 11; 2; 7;
12; 3; 6; 11; 3; 6; 12; 3; 7; 11; 3; 7; 12; 4; 6; 11; 4; 6; 12; 4; 7; 11; 4;
7; 12; 5; 6; 11; 5; 6; 12; 5; 7; 11; 5; 7; 12]
顺便说一句,尽管这不是笛卡尔乘积,但它看起来像是一种相当“正常”的算法,因此与之最接近的典型的知名算法是什么?
答案 0 :(得分:3)
(操作说明:这个高级答案导致我在另一个答案中实现了,谢谢Vivek!)
您拥有的数据集称为N-ary树。
在这里,每个节点/列表元素的子节点数可能在0 <= children <= N
之间。
算法步骤:
lists
的第一个列表。list
。 lists
并将父元素添加到每个返回的子列表中,并将其添加到lists
。lists
。伪代码:
function possibleLists(curr_list){
my_new_lists = [];
for each element in curr_list:
child_lists = possibleLists(element)
for each child_list in child_lists:
child_list.add(element)
my_new_lists.add(child_list)
if child_lists.size() == 0: // needed if there were no further deep levels than the level of curr_list elements
my_new_lists.add(new List().add(child_list)) // you can return current instance to have this kind of chaining.
return my_new_lists;
}
注意:如果要实现尾部递归,则必须将访问的元素的路径作为参数list
传递给参数中的下一个递归调用,以附加到该元素中子元素。
P.S-我不是F#
编码员,所以可以最大程度地帮助您使用伪代码。
答案 1 :(得分:2)
感谢@ vivek_23在N进制树上提供了指针,我读了一些关于树遍历的文章,等等,这并不完全是关于(如果我错了,请纠正我),但是带我去一个简单的,我相信是优雅的解决方案:
let rec nestedApply f acc inp =
match inp with
| [] -> f acc
| head::tail ->
[
for x in head do
yield! nestedApply f (x::acc) tail
]
在这种情况下,apply函数f
作用于每次迭代具有相同长度的小子列表,但是对于我的特殊情况,这并不重要(而且,如果apply-function不需要关心子集的顺序)。要获得与原始问题完全相同的行为,请按以下方式使用它:
> nestedApply List.rev [] [[1 .. 5];[6;7];[11;12]];;
val it : int list =
[1; 6; 11; 1; 6; 12; 1; 7; 11; 1; 7; 12; 2; 6; 11; 2; 6; 12; 2; 7; 11; 2; 7;
12; 3; 6; 11; 3; 6; 12; 3; 7; 11; 3; 7; 12; 4; 6; 11; 4; 6; 12; 4; 7; 11; 4;
7; 12; 5; 6; 11; 5; 6; 12; 5; 7; 11; 5; 7; 12]
稍微整洁的解决方案隐藏了累加器:
let nestedApply f inp =
let rec innerLoop acc inp =
match inp with
| [] -> f acc
| head::tail ->
[
for x in head do
yield! innerLoop (x::acc) tail
]
innerLoop [] inp