在Erlang中生成没有堆栈的powerset

时间:2011-12-24 23:12:08

标签: erlang subset powerset

注意:这是我之前关于powersets的问题的续集。

我有一个很好的Ruby解决方案,我以前的question关于生成一个集合的powerset而不需要保持堆栈:

class Array
  def powerset
    return to_enum(:powerset) unless block_given?
    1.upto(self.size) do |n|
      self.combination(n).each{|i| yield i}
    end
  end
end
# demo
['a', 'b', 'c'].powerset{|item| p item} # items are generated one at a time
ps = [1, 2, 3, 4].powerset # no block, so you'll get an enumerator 
10.times.map{ ps.next } # 10.times without a block is also an enumerator

它完成了这项工作并且运作良好。

但是,我想尝试在Erlang中重写相同的解决方案,因为对于{|item| p item}块,我已经在Erlang中编写了大部分工作代码(它对每个生成的子集执行一些操作)。

虽然我对Erlang有一些经验(我已经阅读了所有2本书:)),但我对example以及sepp2k友好地给我以前关于powersets的问题的评论感到困惑。我不明白这个例子的最后一行 - 我唯一知道的是列表理解。我不明白如何修改它以便能够对每个生成的子集执行某些操作(然后将其抛出并继续下一个子集)。

如何在Erlang中重写这个Ruby迭代的powerset生成?或者提供的Erlang示例已经几乎适合需要了?

1 个答案:

答案 0 :(得分:1)

所有给定的examples都有O(2 ^ N)内存复杂度,因为它们返回整个结果(第一个例子)。最后两个示例使用常规递归,以便堆栈引发。下面的代码是修改和编译的例子,你可以做你想做的事情:

subsets(Lst) ->
    N = length(Lst),
    Max = trunc(math:pow(2, N)),
    subsets(Lst, 0, N, Max).

subsets(Lst, I, N, Max) when I < Max ->
    _Subset = [lists:nth(Pos+1, Lst) || Pos <- lists:seq(0, N-1), I band (1 bsl Pos) =/= 0],
    % perform some actions on particular subset
    subsets(Lst, I+1, N, Max);
subsets(_, _, _, _) ->
    done.

在上面的片段中,尾部递归被Erlang编译器优化并转换为简单的迭代。只有当递归调用是函数执行流程中的最后一个调用时,才能以这种方式优化递归。另请注意,每个生成的子集可用于代替注释,并且在此之后将被遗忘(垃圾收集)。由于堆栈和堆都不会增长,但是你也必须一个接一个地对子集进行操作,因为没有包含所有这些的最终结果。

我的代码对示例中的类似变量使用相同的名称,以便更容易地比较它们。我确信它可以针对性能进行改进,但我只是想表达这个想法。