WORDLEで覚える肯定先読み・否定先読みの正規表現

IT

WORDLE流行ってますね色々攻略を考えてみたわけですが、まあ辞書が分かってるなら正規表現で検索しちゃえば答えは出るわけで。最初の単語としてTOWNSを入力して、以下の状態になったとします。

で次の候補を探す時に、

  • 2文字目がo → grep ^.o
  • tsが含まれる → grep t | grep s | grep -v ^t | grep -v ^….s

とパイプで繋ぎまくって検索するのは正規表現の使いこなしとしては切ないものがあるので、もうちょっとだけ便利な方法を覚えておこう、というのが本稿の趣旨。

肯定先読みと否定先読みを使うだけです。

ただしmacOSに入ってるgrepは弱々なので、以下の正規表現を使う場合はVisual Studio Codeを使うとか、brew install grepしてGNU grepをインストールしてgrep -Pすること。

肯定先読み

形としては以下。これでpatternとマッチする「位置」が指定される。

(?=pattern)

肯定先読み、という訳があまり良くなくて理解を妨げやすいのだけれど、文字列の先頭を表す^(キャレット)や末尾を表す$(ダラー)と同じだと思えば良い。つまりパターンに一致する位置を示すだけだ。

2文字目がoである単語を検索するには

^(?=.o...)[a-z]{5}$

とすれば良い。以下も等価。

^(?=.o.{3})[a-z]{5}$

読み下すと、

^ (文字列の途中からではなく)文字列の先頭から
(?=.o...) [a-z]{5}を先読みして2文字目にoがマッチする位置、つまり(この場合)文字列の先頭から
[a-z]{5} 英小文字の5文字繰り返し
$ で終わる文字列

Wordleの辞書は全て5文字の英単語に限られるので、量指定子で{5}としているから^(先頭)と$(末尾)は不要だけれど、一応つけておいた。

やってみよう。

% grep -P '^(?=.o...)[a-z]{5}$' wordle.txt | head -10
aorta
boabs
boaks
board
boars
boart
boast
boats
bobac
bobak

ここだけなら実は

% grep .o...

と何ら違いは無い。でも、この後で説明する否定先読みと合わせて使うには、肯定先読みの書き方も知っておいが方が便利だったりする。

否定先読み

もう一つが否定先読み。これでpatternとマッチしない「位置」が指定される。

(?!pattern)

肯定先読みが理解出来れば、これはその反対。上の例では、tは含まれているが先頭ではない、つまりWordleで黄色になった文字を含む単語を検索するのに使える。

^(?=.*t)(?!t....)[a-z]{5}$

とすれば良い。読み下すと、

^ (文字列の途中からではなく)文字列の先頭から
(?=.*t) この後に続く文字列を先読みして、0以上の長さの文字(.*)の後にtが含まれる=1〜5文字目のいずれかがtである位置から
(?!t....) [a-z]{5}を先読みして先頭にtがマッチしない位置、つまり(この場合)文字列の先頭から
[a-z]{5} 英小文字の5文字繰り返し
$ で終わる文字列

これもやってみよう。

% grep -P '^(?=.*t)(?!t....)[a-z]{5}$' wordle.txt | head -10
aarti
abaft
abate
abbot
abets
ablet
abort
about
absit
abuts

これで黄色になった文字はOK。さて、緑にも黄にもならなかった、黒い文字はどうするかと言えば、

^(?!.*w)[a-z]{5}$

とすれば良い。つまり、肯定先読み、否定先読みを組み合わせればWordleに勝つる、と。

組み合わせる

ここまでの正規表現をそのまま列挙すれば、検索が出来る。

^(?=.*t)(?!t....)(?=.o...)(?!.*w)(?!.*n)(?=.*s)(?!....s)[a-z]{5}$

やってみよう。残りは42単語だ。12,972単語からここまで絞り込めた。

% grep -P '^(?=.*t)(?!t....)(?=.o...)(?!.*w)(?!.*n)(?=.*s)(?!....s)[a-z]{5}$' wordle.txt 
boast
boost
coast
coost
coset
costa
coste
doest
foist
goest
gosht
hoast
hoist
horst
hosta
joist
joust
loast
lotsa
moist
moste
moust
posit
potsy
roast
roist
roost
roset
rosit
rosti
roust
royst
softa
softy
soote
sooth
sooty
sorta
sotol
souct
south
zoist

もっと短くすることも可能だけれど、本稿の目的はそこではないので悪しからず。とりあえず、Wordleで肯定先読みと否定先読みが理解できたら幸いです。

タイトルとURLをコピーしました