全ての組み合わせ
Posted feedbacks - Ruby
配列でお茶を濁すと…
1 2 3 4 5 6 7 8 9 10 11 12 | def cross_product(*arrays)
if arrays.length == 1
arrays.first.map{|x| [x]}
else
cross_product(*arrays[1..-1]).inject([]) {|result, x|
result.concat arrays.first.map{|a| [a, *x] }
}
end
end
cross_product [1,2,3,4], %w[a b c] # => [[1, "a"], [2, "a"], [3, "a"], [4, "a"], [1, "b"], [2, "b"], [3, "b"], [4, "b"], [1, "c"], [2, "c"], [3, "c"], [4, "c"]]
cross_product [0,1], %w[a b], ["Foo", "Bar"] # => [[0, "a", "Foo"], [1, "a", "Foo"], [0, "b", "Foo"], [1, "b", "Foo"], [0, "a", "Bar"], [1, "a", "Bar"], [0, "b", "Bar"], [1, "b", "Bar"]]
|
文字列を1文字ずつ無理矢理取り出すにはこんな細工をする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Array
alias :_by_element :to_a
end
class String
def _by_element
split(//)
end
end
def cross_product(*sequences)
if sequences.length == 1
sequences.first._by_element.map{|x| [x]}
else
cross_product(*sequences[1..-1]).inject([]) {|result, x|
result.concat sequences.first._by_element.map{|a| [a, *x] }
}
end
end
cross_product [1,2,3,4], %w[a b c] # => [[1, "a"], [2, "a"], [3, "a"], [4, "a"], [1, "b"], [2, "b"], [3, "b"], [4, "b"], [1, "c"], [2, "c"], [3, "c"], [4, "c"]]
cross_product [0,1], "ab", ["Foo", "Bar"] # => [[0, "a", "Foo"], [1, "a", "Foo"], [0, "b", "Foo"], [1, "b", "Foo"], [0, "a", "Bar"], [1, "a", "Bar"], [0, "b", "Bar"], [1, "b", "Bar"]]
|
再帰的に
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | def cross_product(*args)
raise ArgumentError if args.size <= 1
if args.size == 2
r = []
args.first.each{ |v|
args.last.each{ |vv|
r << [v, vv]
}
}
return r
end
cross_product(args.first, cross_product(*args[1..-1])).map{ |rr|
[rr.first] + rr.last
}
end
def cross_product_wrap(*args)
args.map!{ |arg|
case
when arg.kind_of?(String)
arg.scan(/./)
when arg.kind_of?(Array)
arg
else
[arg]
end
}
cross_product(*args)
end
|
関数型でなくArrayクラスのクラスメソッドとして作成してみた (主体がはっきりしてないと落ち着かないので) . 渡したリストが配列でなくても動作するようにしたけど副作用がありそうな気がする.
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Array
def self.cross(*argv)
argv.map!{|x| x.class.to_s == new.class.to_s ? x : x.to_s.split(//)}
ary = argv.shift.map{|x| [x]}
argv.each{|x|
ary = new(ary.size*x.size){|j|
[x[j.modulo(x.size)], *ary[j.div(x.size)]]
}
}
ary.map{|x| x.reverse}
end
end
p Array.cross([0, 1], "ab", ["Foo", "Bar"])
|




zigorou #3400() Rating0/0=0.00
Pythonで表現すると下のようになります。
順番はこの通りでなくても構いません。返すものはリストと書きましたが、 なんらかの「一度に全部をメモリ上に作成しないリスト状のモノ」がある言語ではそちらを使う方がおすすめです。 数値や文字列を一つのリストに混在させるのがやっかいな言語では整数のリストに限定しても構いません。このお題はZIGOROuさんとのやりとりにヒントを得て作りました。 (しまった、先にブログで公開されてしまった→Yet Another Hackadelic - 直積の導出と考えうる全ての値を網羅したハッシュの生成)
追記:サンプル出力が間違っていたのでoceanさんの解答を使って出力し直しました。
[ reply ]