【ITニュース解説】Weekly Challenge: Reversing my broken keys
2025年10月05日に「Dev.to」が公開したITニュース「Weekly Challenge: Reversing my broken keys」について初心者にもわかりやすく解説しています。
ITニュース概要
毎週のプログラミング課題として、壊れたキーでは打てない文字を含まない単語が文章中にいくつあるかを数える問題と、文字列の指定文字までを逆順にする問題の解法を、PythonとPerlのコードで解説する。
ITニュース解説
プログラミングの学習では、実際に手を動かして課題を解決することが非常に重要である。毎週出されるプログラミング課題「The Weekly Challenge」は、そうした練習の機会を提供しており、今回の記事では二つの課題とその解決策がPythonとPerlという二つのプログラミング言語で示されている。これらの課題を紐解くことで、プログラミングの基本的な考え方や文字列操作の方法を学ぶことができる。
一つ目の課題は「Broken Keyboard(壊れたキーボード)」である。この課題は、与えられた英文と、壊れて使えないキー(文字)のリストから、完全にタイプできる単語の数を数えるというものだ。例えば、「Hello World」という文章と壊れたキー「d」が与えられた場合、「Hello」はタイプできるが「World」は「d」が含まれるためタイプできない。したがって、答えは1となる。
この課題を解決するための考え方はこうである。まず、大文字と小文字の区別をなくすために、入力された文章と壊れたキーの文字をすべて小文字に変換する。これにより、「A」と「a」を同じ文字として扱えるようになり、処理が単純化される。次に、タイプできた単語の数を数えるための変数(カウンター)を0で初期化する。そして、入力された文章を単語ごとに区切り、それぞれの単語について以下のチェックを行う。その単語の中に、壊れたキーのリストに含まれる文字が一つでも存在するかどうかを調べる。もし一つも含まれていなければ、その単語は完全にタイプできると判断し、カウンターの値を1増やす。すべての単語をチェックし終えたら、最終的なカウンターの値が結果となる。
Pythonでの実装を見てみよう。broken_keyboardという関数が定義されており、引数として文章の文字列と壊れたキーのリストを受け取る。まず、input_string.lower()で文章全体を小文字にし、broken_keys = [key.lower() for key in broken_keys]で壊れたキーのリストもすべて小文字にする。completed_wordsという変数を0で初期化し、for word in input_string.split():で文章をスペースで区切って単語ごとにループ処理を行う。if not any(char in broken_keys for char in word):という部分が単語のチェックにあたる。any()関数は、指定された条件に合致する要素が一つでもあれば真(True)を返す。ここでは、「単語の各文字charが壊れたキーbroken_keysに含まれるか?」という条件をチェックしている。もし「一つでも壊れたキーが含まれている」ならばany()は真を返し、notがついているため偽(False)となる。つまり、if文の条件は「壊れたキーが一つも含まれていない」ときに真となる。この条件が真ならばcompleted_words += 1でカウンターを増やす。
Perlでの実装も同様のロジックに従う。mainサブルーチン(関数に相当)が定義されており、lc関数で文字列を小文字に変換する。split ' ', $input_stringで文字列を単語に分割し、for my $word (...)で単語ごとにループ処理を行う。if (not grep { index($word, $_) != -1 } @broken_keys)が単語のチェック部分である。grep関数はリストの中から条件に合う要素を抽出するが、ここでは条件に合う要素があれば真を返すリストコンテキストで使われている。index($word, $_) != -1は、現在の単語$wordの中に壊れたキー$_が含まれていれば、その位置を返す(見つからなければ-1)。つまり、「単語の中に壊れたキーが含まれている」ときに真となる。notが付いているため、「単語の中に壊れたキーが一つも含まれていない」ときに真となり、$completed_words++でカウンターが増える。
二つ目の課題は「Reverse Prefix(プレフィックスの反転)」である。この課題は、与えられた文字列と特定の文字に対し、その文字が文字列内で最初に出現するまでの部分(プレフィックス)を反転させ、残りの文字列と結合して新しい文字列を返すというものだ。例えば、「programming」という文字列と文字「g」が与えられた場合、「g」が最初に出現するまでの部分「programmin」を反転させると「nimmargorp」となり、元の文字列の残りの部分(「g」以降の文字)がないため、結果は「nimmargorp」となる。ただし、例では「programming」と「g」の組み合わせで「gorpramming」とあるので、指定された文字自身もプレフィックスに含まれ、その文字までを反転させるという解釈である。「programming」の最初の'g'までの部分は「prograg」。これを反転させると「gargorp」となる。残りの「ramming」と結合すると「gargorpramming」になるはずだが、記事の例では「gorpramming」となっているため、これは「g」が出現する位置までの部分(「progra」)を反転させ、その後に「g」をつけ、残りの「ramming」を結合した結果か、あるいは元の文字列のどこかから「g」を見つけるまでを反転するがその「g」自身は反転部分の末尾となり、その後ろの文字列はそのままという解釈だろう。記事のPythonコード input_string[pos::-1] と Perlコード substr( $input_string, 0, $pos + 1 ) から、指定された文字自身も反転対象のプレフィックスに含まれるという解釈が正しい。例えば「programming」と「g」の場合、「g」はインデックス6に最初に出現する。Pythonではinput_string[6::-1]でインデックス6から0までの部分(「g」から「p」まで)を反転させることになる。これは「gnimmargorp」となる。しかし、例の出力「gorpramming」とは合致しない。これは元の記事の例と解説の間に微妙なずれがある可能性を示唆している。ここでは、記事のコードが示すロジックに基づき「指定された文字まで(その文字を含む)のプレフィックスを反転させる」として解説を進める。
この課題の解決の考え方はこうだ。まず、与えられた文字列の中で、指定された文字が最初に出現する位置を見つける。もしその文字が文字列中に存在しない場合は、エラーとして処理する。文字が見つかったら、文字列の先頭からその文字の出現位置まで(その文字自身を含む)の部分を抜き出す。抜き出した部分を反転させ、その後ろに、元の文字列の残りの部分(指定された文字の次から文字列の最後まで)を結合する。
Pythonでの実装を見てみよう。reverse_prefixという関数が定義されており、文字列input_stringと文字charを受け取る。pos = input_string.find(char)で、charがinput_stringの中で最初に出現するインデックス(位置)をposに格納する。find()メソッドは、見つからない場合は-1を返すので、if pos == -1:でエラーチェックを行う。反転処理はreturn input_string[pos::-1] + input_string[pos+1:]で行われる。input_string[pos::-1]はPythonのスライス記法であり、文字列のインデックスposから先頭(インデックス0)までを、逆順(ステップ-1)で抜き出して反転させるという意味になる。input_string[pos+1:]は、charが出現した位置の次から文字列の最後までを抜き出す。これら二つの部分を+演算子で結合して、新しい文字列として返す。
Perlでの実装も同様である。mainサブルーチンで文字列$input_stringと文字$charを受け取る。my $pos = index( $input_string, $char );で、$charが$input_stringの中で最初に出現するインデックスを$posに格納する。index()関数は、見つからない場合は-1を返す。反転処理はsay reverse( substr( $input_string, 0, $pos + 1 ) ) . substr( $input_string, $pos + 1 );で行われる。substr( $input_string, 0, $pos + 1 )は、$input_stringのインデックス0から$pos + 1文字分(つまり、charが出現した位置までを含む部分)を抜き出す。この抜き出した文字列をreverse()関数で反転させる。.演算子は文字列結合を表す。substr( $input_string, $pos + 1 )は、$input_stringの$pos + 1番目の文字から文字列の最後までを抜き出す。これらの処理を結合して結果を出力する。
これらのプログラミング課題は、文字列の操作、条件分岐、ループ処理といったプログラミングの基本的な要素を理解し、実際にコードを書いて問題を解決する力を養うのに非常に役立つ。システムエンジニアを目指す上では、このような基本的なアルゴリズムを理解し、様々なプログラミング言語で実装できる能力が求められるため、実践的な練習として最適である。