順列の生成とList内包表記
この間のErlang勉強会で勉強した順列を生成する関数をいろいろと移植してみました。まず元のErlangの実装は以下。与えられたリストから要素を1つ取り出して、残りの要素から再帰的に順列を求めて、それらを結合するアルゴリズムです。ふむ、とてもシンプルです。
perms([]) -> [[]]; perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].
まずHaskellの場合。Erlangと似たようなものです。でも型があると何か落ち着きます。ふつう遅延評価なので大きなリストを渡しても難しいこと考えなくてよさそうです。
module Main (main) where import Data.List ((\\)) perms :: Eq a => [a] -> [[a]] perms [] = [[]] perms xs = [ h : t | h <- xs, t <- perms (xs \\ [h]) ] main :: IO () main = print $ perms [1, 2, 3] -- => [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
次。Rubyの場合。んー、いまいちぐっとこないなー。
#!/usr/bin/env ruby class Array def perms return [[]] if empty? inject([]) do |rs, h| rs + (self - [h]).perms.map {|t| [h] + t } end end end p [1, 2, 3].perms # => [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
やっぱりリスト内包表記か?ということで、うじひさくんのアレを使ってみました。おお、いい感じです。実用じゃないけど…
#!/usr/bin/env ruby # リスト内包表記 # 2007-06-01 Tatsuhiro Ujihisa # # List comprehension - Wikipedia, the free encyclopedia # http://en.wikipedia.org/wiki/List_comprehension class String def lisc /^\{\s*(.*?)\s*\|\s*(.*?)\s*\}$/ =~ self left, right = $1, $2 comma = /,\s/ # セパレータとしてのコンマは、スペース必須 rights = right.split(comma) vars, conds = rights.map {|right| right.split(/\s*<-\s*/) }.partition {|right| right.size == 2 }. map {|right| right.to_a} ["_list_comprehension_tmp = []", vars.map {|var| "#{var[1]}.each {|#{var[0]}| " }.join, " _list_comprehension_tmp << #{left}" + (conds.empty? ? '' : " if " + conds.join(' && ')) + vars.map { "}" }.join, "_list_comprehension_tmp"].join "\n" end end class Array def perms return [[]] if empty? eval '{ [h] + t | h <- self, t <- (self - [h]).perms }'.lisc end end p [1, 2, 3].perms # => [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]