まず、Erlangの再帰的解法は必ずしも余分なスタックを消費することを意味するわけではありません。メソッドが末尾再帰的である(すなわち、再帰呼び出しが最後のものである)場合、コンパイラはパラメータを修正してメソッドの先頭にジャンプする。これは関数型言語のためのかなり標準的です。
すべての数字AからBのリストを生成するには、ライブラリメソッドlists:seq(A, B)
を使用します。
値リスト(0から2^N-1までのリストなど)を別の値リスト(バイナリ表現から生成されたセットなど)に変換するには、lists:map
またはlist comprehensionを使用します。
数値をバイナリ表現に分割するのではなく、それを回して、対応するビットが各M値(0〜2^N-1)に設定されているかどうかを確認することをお勧めします-of-2-bitmasks。次に、ビットが設定されているかどうかをバイナリANDで確認できます。しかし、あなたはまた、スタック領域を消費することなく末尾再帰を使用して同じことを達成することができます
generate_powerset(List) ->
% Do some pre-processing of the list to help with checks later.
% This involves modifying the list to combine the element with
% the bitmask it will need later on, such as:
% [a, b, c, d, e] ==> [{1,a}, {2,b}, {4,c}, {8,d}, {16,e}]
PowersOf2 = [1 bsl (X-1) || X <- lists:seq(1, length(List))],
ListWithMasks = lists:zip(PowersOf2, List),
% Generate the list from 0 to 1^N - 1
AllMs = lists:seq(0, (1 bsl length(List)) - 1),
% For each value, generate the corresponding subset
lists:map(fun (M) -> generate_subset(M, ListWithMasks) end, AllMs).
% or, using a list comprehension:
% [generate_subset(M, ListWithMasks) || M <- AllMs].
generate_subset(M, ListWithMasks) ->
% List comprehension: choose each element where the Mask value has
% the corresponding bit set in M.
[Element || {Mask, Element} <- ListWithMasks, M band Mask =/= 0].
:一緒にそのすべてを置く
は、次のような解決策を得ます。また、0〜2^N-1のリストを生成したり、保持したりする必要はありません。
generate_powerset(List) ->
% same preliminary steps as above...
PowersOf2 = [1 bsl (X-1) || X <- lists:seq(1, length(List))],
ListWithMasks = lists:zip(PowersOf2, List),
% call tail-recursive helper method -- it can have the same name
% as long as it has different arity.
generate_powerset(ListWithMasks, (1 bsl length(List)) - 1, []).
generate_powerset(_ListWithMasks, -1, Acc) -> Acc;
generate_powerset(ListWithMasks, M, Acc) ->
generate_powerset(ListWithMasks, M-1,
[generate_subset(M, ListWithMasks) | Acc]).
% same as above...
generate_subset(M, ListWithMasks) ->
[Element || {Mask, Element} <- ListWithMasks, M band Mask =/= 0].
サブセットのリストを生成するときは、リストの先頭に新しい要素を追加することになります。リストは単独でリンクされ、不変なので、最初からどこにでも要素を配置したい場合は、リストをコピーする "次の"ポインタを更新する必要があります。そのため、ヘルパー関数はAcC++ [generate_subset(...)]
の代わりにAcc
リストを末尾に置きます。この場合、アップの代わりにカウントダウンしているので、すでに逆順になっているので、同じ順序で出てきます。
だから、結論では、Erlangでループ
- は慣用的に末尾再帰関数を介して行わまたは
lists:map
のバリエーションを使用しています。
- Erlangを含む多くの(ほとんどの)関数言語では、末尾再帰はジャンプを使用して実装されているため余分なスタック領域を消費しません。
- リスト構築は、効率の理由から、通常、後方に行われる(すなわち、
[NewElement | ExistingList]
)。
- リスト内のN番目のアイテム(
lists:nth
を使用)を検索したくないのは、リストが1つにリンクされているためです。リストを何度も繰り返して繰り返す必要があります。代わりに、上記のビットマスクをどのように前処理したかなど、リストを一度反復する方法を見つけてください。
質問はどうかわかりません。指定された集合のすべてのポーズ可能な部分集合の集合を作成します。グレーコードはある数字から別の数字へのマッピングです。これら2つの用語の間で共通点は何ですか?あなたは何を達成したいのですか?いくつかの例を挙げることができますか? – werewindle
@werewindle、私は質問を更新しました。 – skanatek
私は参照してください。つまり、0から2^N-1までの数字が生成され、各数字の別のものは次のことを行います。数字の各(n番目の)ビットに対して、設定されているかどうかテストされます。ビットが設定されている場合、元のセットから対応する(n番目の)要素を結果セットに追加する必要があります。 – werewindle