我正在尝试将erlang Mastermind解算器编码为练习(我是一个完整的新手,但我认为这是一个功能性语言的有趣练习)
我希望它尽可能一般,所以我觉得我需要一个笛卡尔幂函数。类似的东西:
cart_pow([a,b],2) -> [[a,a],[a,b],[b,a],[b,b]]
cart_pow([a,b],3) -> [[a,a,a],[a,a,b],[a,b,a],[a,b,b],[b,a,a],[b,a,b],[b,b,a],[b,b,b]]
我想不出纯粹的功能(递归,地图,折叠......)解决方案。 有线索吗?奖金,如果它是懒惰的。
答案 0 :(得分:3)
@ Ed'ka提供的解决方案简洁而且很好,但尽管如此,其复杂性为O(N)
。
我建议你考虑一下Exponentiation by squaring method,它在计算能力方面提供O(log(N))
复杂性。使用这种技术,可以用这种方式实现笛卡尔幂:
%% Entry point
cart(List, N) ->
Tmp = [[X] || X <- List],
cart(Tmp, Tmp, N).
cart(_InitialList, CurrList, 1) ->
CurrList;
cart(_InitialList, CurrList, N) when N rem 2 == 0 ->
Tmp = mul(CurrList, CurrList),
cart(Tmp, Tmp, N div 2);
cart(InitialList, CurrList, N) ->
Tmp = cart(InitialList, CurrList, N - 1),
mul(InitialList, Tmp).
mul(L1, L2) ->
[X++Y || X <- L1, Y <- L2].
P.S。从shell(我将函数cart
打包到mudule my_module
)的用法示例:
1> c(my_module).
{ok,my_module}
2>
2> my_module:cart([0,1], 2).
[[0,0],[0,1],[1,0],[1,1]]
3>
3> my_module:cart([0,1], 3).
[[0,0,0],
[0,0,1],
[0,1,0],
[0,1,1],
[1,0,0],
[1,0,1],
[1,1,0],
[1,1,1]]
答案 1 :(得分:2)
从Haskell实施:
cart_pow(Xs, N) ->
sequence(lists:duplicate(N, Xs)).
sequence([]) ->
[[]];
sequence([Xs|Xss]) ->
[[X|Xs1] || X <- Xs, Xs1 <- sequence(Xss)].
不确定如何使Erlang的列表变得懒惰。
<强>更新强> 通过简单地使尾递归(即使我相信这三者之间没有渐近的差异),可以在性能方面改进这个版本。
cart_pow(Xs, N) ->
sequence(lists:duplicate(N, Xs)).
sequence(Xss) ->
sequence(Xss, [[]]).
sequence([], Acc) ->
Acc;
sequence([Xs|Xss], Acc) ->
sequence(Xss, [[X|Xs1] || X <- Xs, Xs1 <- Acc]).
与@ stemm的版本相比:
1> timer:tc(fun() -> length(tmp1:cart([0,1], 20)) end).
{383939,1048576}
2> timer:tc(fun() -> length(tmp1:cart_pow([0,1], 20)) end).
{163932,1048576}
PS:甚至更好:
sequence(Xss) ->
lists:foldl(fun(Xs, A) -> [[X|Xs1] || X <- Xs, Xs1 <- A] end, [[]], Xss).
答案 2 :(得分:1)
您可能会发现此Stack Overflow问题很有用,它涉及在函数式语言中生成列表的笛卡尔幂。问题针对F#,但评论中也有一个Haskell示例:F#: how to find Cartesian power