Haskellでたわむれていると。 id:nobsun さんが実に軽快な解法をコメントに残してくださいました。
colns = concat cns where cns'@(_:cns) = [""] : [[c:ns | c <- ['A'..'Z'], ns <- nss] | nss <- cns' ]
Qiita では、さらにエレガントな解を展開されていますので、ぜひそのシンプルなコードを見てみてください。
[Char] と [String] から [String]
A〜Zの並びをかけ合わせるためにわたしが取った方法は、文字列のリストのリストに sequence
を適用するというものでした。
Prelude> sequence [ [ "A", "B", "C" ], [ "A", "B", "C" ] ] [["A","A"],["A","B"],["A","C"],["B","A"],["B","B"],["B","C"],["C","A"],["C","B"],["C","C"]]
(A〜Zの文字を使うと結果が大きくなってしまうため、A〜Cの文字で説明しています。以下同様)
ただし、これだと生成されるのは文字列のリストのリストになってしまいます。
そのためリストの各要素(文字列のリスト)を連結してやる必要がありました。
Prelude> map concat $ sequence [ [ "A", "B", "C" ], [ "A", "B", "C" ] ] ["AA","AB","AC","BA","BB","BC","CA","CB","CC"]
この方法をリスト内包表記で書き換えると、おおよそ次のようになります。
Prelude> [ concat [x, y] | x <- [ "A", "B", "C" ], y <- [ "A", "B", "C" ] ] ["AA","AB","AC","BA","BB","BC","CA","CB","CC"]
ここで String
の連結のために concat [x, y]
としていますが、x
が Char
であれば x:y
と書くことができます。
また x
に文字を取り出すのであれば x <- [ "A", "B", "C" ]
は x <- ['A'..'C']
と書けばよいことになります。
Prelude> [ x:y | x <- ['A'..'C'], y <- [ "A", "B", "C" ] ] ["AA","AB","AC","BA","BB","BC","CA","CB","CC"]
自分の内包表記に自分を使う
y
の値を取り出しているリスト [ "A", "B", "C" ]
は、上の式の [ "A", "B", "C" ]
を [""]
に置き換えることで得ることができます。
[ x:y | x <- ['A'..'C'], y <- [""] ] ["A","B","C"]
これを cns_0
と置くと、
Prelude> let cns_1 = [ x:y | x <- ['A'..'C'], y <- cns_0 ] Prelude> cns_1 ["A","B","C"]
先ほどの式を cns_2
とすると、
Prelude> let cns_2 = [ x:y | x <- ['A'..'C'], y <- cns_1 ] Prelude> cns_2 ["AA","AB","AC","BA","BB","BC","CA","CB","CC"]
となります。
cns_0 = [""]
とし、f s = [ x:y | x <- ['A'..'C'], y <- s ]
とすれば、
cns_0 = [""] cns_1 = f cns_0 = ["A","B","C"] cns_2 = f cns_1 = ["AA","AB","AC","BA","BB","BC","CA","CB","CC"] ...
となり、[ cns_1, cns_2 ]
というリストを得たいばあい、f
に [ cns_0, cns_1 ]
の要素を次々に適用して得られる値をリストにすればよいことになります。
Prelude> [ [ x:ns | x <- ['A'..'C'], ns <- nss] | nss <- [ cns_0, cns_1 ] ] [["A","B","C"],["AA","AB","AC","BA","BB","BC","CA","CB","CC"]]
つまり [ cns_1, cns_2, ... ]
というリストを得たいばあい、f
に [ cns_0, cns_1, ... ]
の要素を次々に適用して得られる値をリストにすればよく、これはつまり得たいリストの先頭に cns_0
を付加したリストになります。
Prelude> let cns' = [""]:[ [ x:ns | x <- ['A'..'C'], ns <- nss] | nss <- cns' ] Prelude> take 4 cns' [[""],["A","B","C"],["AA","AB","AC","BA","BB","BC","CA","CB","CC"],["AAA","AAB","AAC","ABA","ABB","ABC","ACA","ACB","ACC","BAA","BAB","BAC","BBA","BBB","BBC","BCA","BCB","BCC","CAA","CAB","CAC","CBA","CBB","CBC","CCA","CCB","CCC"]]
このリストの要素を連結し先頭の空文字列を削除すれば、最終的に欲しかった文字列のリストが得られることになります。
Prelude> let colns = tail $ concat cns' where cns' = [""]:[ [ x:ns | x <- ['A'..'C'], ns <- nss] | nss <- cns' ] Prelude> take 39 colns ["A","B","C","AA","AB","AC","BA","BB","BC","CA","CB","CC","AAA","AAB","AAC","ABA","ABB","ABC","ACA","ACB","ACC","BAA","BAB","BAC","BBA","BBB","BBC","BCA","BCB","BCC","CAA","CAB","CAC","CBA","CBB","CBC","CCA","CCB","CCC"]
いかにして先頭の要素を落とすか
tail
関数を使って。
Prelude> tail [1,2,3] [2,3]
パタンマッチングを使って。
Prelude> let h:t = [1,2,3] Prelude> h 1 Prelude> t [2,3]
パタンマッチングを使って(元の値も使いたいとき)。
Prelude> let a@(h:t) = [1,2,3] Prelude> h 1 Prelude> t [2,3] Prelude> a [1,2,3]