質問: "私は<アイスクリーム|アイスキャンデー|アイス>が好き(なの)です。" ``` とします。 1. `<>`に囲まれた部分は、どれでもいいが、線で区切られた単語のうち、この中のどれか一つは書かれていなければならないとします。 2. `()`に囲まれた文字は、あってもなくても構わないとします。 このとき、1と2を満たす組み合わせとなる文を作りたいと思っています。 まず、どれか3つのうちで3通り。`()`はあるかないかで2通り。 計6通りの文章が出来上がると思います。 この6通りを瞬時に算出するプログラムを書きたいのですが、 どうすればいいでしょうか。なんなら、`<>`などは別の記号に置き換えても もちろん問題ありません。 環境: python3.6
回答: ### 単語候補リストの作成 まずは、`< | >` や `( )` といった記号を用いた独自の記法をパースして、文を構成する単語の候補になるリストたちを作成します。(パース部分に関しては、この回答では扱いません) * 必須: 「`私は`」のように何にも囲まれていない出現が必須の単語。 これは要素数1の単語候補リストになります。 リスト表現: `["私は"]` * 選択: 「`<アイスクリーム|アイスキャンデー|アイス>`」のように `< >` で囲まれた、どれかを選択する単語たち。 これはそのまま、単語候補リストになります。 リスト表現: `["アイスクリーム", "アイスキャンデー", "アイス"]` * 任意: 「`(なの)`」のように `( )` で囲まれた、有っても無くてもよい単語。 これは空文字とこの単語とを選択させる `<なの|>` と同義ですから、2要素の単語候補リストになります。 リスト表現: `["なの", ""]` * 任意単語を含む選択: 「`<>`の中にも`()`のあるパターン」というのは、たとえば `<(なの)|なのかも知れない>` のようなものだと思いますが、これは選択に空文字を含めた `<なの||なのかも知れない>` と同義です。 リスト表現: `["なの", "", "なのかも知れない"]` これにより独自記法の、 ``` "私は<アイスクリーム|アイスキャンデー|アイス>が好き(なの)です。" ``` は5つのリスト、 ``` ["私は"], ["アイスクリーム", "アイスキャンデー", "アイス"], ["が好き"], ["なの", ""], ["です。"], ``` となります。 ### 組み合わせ 目的はおそらく、単語候補リストのいわゆる「直積」を求めることです。 「直積 アルゴリズム」などでWebを検索すると解説があると思いますが、以下では `itertools.product` をそのまま使っています。 (`return` で返しているのは内包表記で生成したイテレータです。解り難ければループなどに展開して理解して下さい) ``` #!/usr/bin/python3 import itertools def gen_sentences(*word_lists): return ("".join(words) for words in itertools.product(*word_lists)) if __name__ == "__main__": sentences = gen_sentences( ["私は"], ["アイスクリーム", "アイスキャンデー", "アイス"], ["が好き"], ["なの", ""], ["です。"], ) for s in sentences: print(s) ``` 出力: ``` 私はアイスクリームが好きなのです。 私はアイスクリームが好きです。 私はアイスキャンデーが好きなのです。 私はアイスキャンデーが好きです。 私はアイスが好きなのです。 私はアイスが好きです。 ```