この記事の内容
この記事ではPaizaのRankD相当の「文字の一致」のコードを16行から1行に短縮することを通して、基本的なコーディングについて学びます。初学者でも簡単に理解できるようステップバイステップで解説したいと思います。
問題
概要
与えられた2つの文字列が一致していればOK、一致していなければNGと出力せよ。
詳細
文字列が一致するとは?
そもそも2つの文字列A,Bが一致しているとは次のように表現することができます。
- 文字列Aと文字列Bの長さが等しい
- 文字列Aと文字列Bのi文字目がすべて等しい
このことを利用して素直に実装すると次のようなコードになります。
解答コード(16行)
A = input()
B = input()
equal_flg = True
if len(A) == len(B):
for i in range(len(A)):
if A[i] == B[i]:
continue
else:
equal_flg = False
break
else:
equal_flg = False
if equal_flg:
print("OK")
else:
print("NG")
コードの詳細な解説
- 入力の受け取り
A = input()
B = input()
- 2つの文字列が一致しているかどうかを判断するフラグ変数の初期化
Trueで初期化し、文字列が一致していないと判明した段階でFalseに変更する。
equal_flg = True
- 2つの文字列の長さが等しいかチェック
長さが異なる場合は、文字列としても当然異なる
if len(A) == len(B):
# 略(後述)
else:
equal_flg = False
- 長さが同じ場合に、先頭の文字から順番に一致しているかチェック
一致している場合は、次の文字のチェック
一致していない場合は、文字列として異なるためequal = False
として、ループを抜ける
ここのfor
がbreak
されずに最後までループが回れば文字列として一致していることになる(equal_flg=True
のまま)
文字列に対する補足
Pythonの文字列(string)はインデックスを指定して、1文字だけ取り出すことができます。str = "abcd"
print(str[2]) # c
for i in range(len(A)):
if A[i] == B[i]:
continue
else:
equal_flg = False
break
- 結果の出力
if equal_flg:
print("OK")
else:
print("NG")
コードの削減1
equal_flg
について再考します。
処理をよく見ると、equal_flg
の役割は、一番最後の結果の出力の際の条件分岐だけです。
その処理を次のように変更します。
-
OK
またはNG
を格納するための変数ans
を用意する -
ans = "OK"
として初期化する -
equal_flg
に関する処理をans
で置き換える
具体的な実装は次の様になります。
削減1による解答コード(13行)
A = input()
B = input()
ans = "OK"
if len(A) == len(B):
for i in range(len(A)):
if A[i] == B[i]:
continue
else:
ans = "NG"
break
else:
ans = "NG"
print(ans)
3行削減されました!
コードの削減2
続いて、continue
の処理に注目します。
continue
のところは、特に処理をせずに、次のループ処理を行うだけなので不要な行です。
しかし、単純に次の様にするとエラーが発生します。これは、if A[i] == B[i]:
の中に何も処理が書かれていないためです。
A = input()
B = input()
ans = "OK"
if len(A) == len(B):
for i in range(len(A)):
if A[i] == B[i]:
# continue
else:
ans = "NG"
break
else:
ans = "NG"
print(ans)
そこで、一工夫します。具体的には次のようにします。
- if の条件式を否定して、元のelse文の処理だけを行う
削減2による解答コード(10行)
A = input()
B = input()
ans = "OK"
if len(A) == len(B):
for i in range(len(A)):
if A[i] != B[i]:
ans = "NG"
else:
ans = "NG"
print(ans)
これはif
のあとに、else
をつけることが必須ではないということを利用しています。
また、break
も不要なのでついでに削除しました。(2つの文字列の最後の1文字だけが異なる場合などにおいては、処理が遅くなります。)
3行削減されました!(合計6行)
コードの削減3
そろそろ削減できそうな箇所が少なくなってきたので、Pythonの機能を使うことにします。
実はPythonでは文字列が一致しているかどうかは、==
を使って判定することができます。
つまり、がんばって書いたfor
は不要だったのでした。
削減3による解答コード(6行)
A = input()
B = input()
ans = "OK"
if A != B:
ans = "NG"
print(ans)
かなりシンプルになりました。
4行削減されました!(合計10行)
コードの削減4
まだまだ削減します。次はA=input()
、B=input()
の部分に注目します。A、Bはいずれもinput
のあとで1回しか使っていないため、A、Bという変数に格納する必要がありません。したがって次のように書くことができます。
削減4による解答コード(4行)
ans = "OK"
if input() != input():
ans = "NG"
print(ans)
2行削減しました!(合計12行)
実は、この段階でif
の条件式を==
に戻してOK
とNG
の代入を逆にしても問題ないのですが、話の流れ上このままにしています。
コードの削減5
今までのコードの削減は処理の流れを変えたり、Pythonの機能を使ったりしましたが、ここからは書き方が変わるだけでやっている処理は同じです。(好みや可読性の違いと思ってください)
実は、Pythonには次のようなif文の書き方があります。(他の言語でも似た様な書き方ができることがあります)
条件式が真のときに返す値 if 条件式 else 条件式が偽のときに返す値
これを利用します。
削減5による解答コード(2行)
ans = "NG" if input()!= input() else "OK"
print(ans)
という感じなのですが、ここでさらにもう一工夫して
print("NG" if input()!= input() else "OK")
とすることができ、1行のコードになりました。
15行削減しました!
終わりに
これはいわゆるコードゴルフと呼ばれる遊びですが、言語の特徴を理解していないとなかなか短いコードにすることができません。遊びを通して言語への理解が少しでも深まればいいかなと思っています。
ただし、コードは短ければ良いというものではありません。短すぎると可読性が低下する可能性もあります(逆もしかりです)また、最後の1行解答よりも、最初の16行のコードの方が、似た様な別の問題に対処するときに拡張しやすいというメリットがあるかもしれません。
2024.8.20追記
2024-08-19 19:49に @hisuie08 さまからコメントにて指摘をいただきました。
最後に言及のあった「コードゴルフ」、少々トリッキーですがprint(input()==input()and"OK"or"NG")
と、Pythonの短絡評価を用いたコードが別解として存在します。
これであればif elseの6文字をand orの5文字に短縮でき、if直後の空白も含めると合計2文字の短縮が可能ですね。
Pythonの短絡評価について簡単に説明しておきます。
- 短絡評価の概要
and
やor
を使用している式の左から順に評価をしていって,真偽が定まった段階で評価をやめる。 - 例
-
1==0 and 2==2
は1==0
が偽なので2==2
は評価されない -
2==2 or 1==0
は2==2
が真なので1==0
は評価されない
-
また,短絡評価では,最後に評価された具体的な値が返り値となります。
P = True and "OK"
print(P) # -> OK
Q = False or "NG"
print(Q) # -> NG
なお,OK
,NG
はいずれもTrueと判断されます.
文字列の真偽値についてはこちら
コードゴルフについて
本記事ではコード量をコードの行数で比較していましたが,最後に言及したコードゴルフでは,コードの文字数でコード量を比較します.
2024.8.21さらに追記
@fujitanozomu さま、@ryusuke920 さまからもコメントをいただきました.
せっかくなのでお二人に紹介していただいたコードについても解説します。
まずは @fujitanozomu さまのほうから。
print(["NG","OK"][input()==input()])
こちらは,["NG","OK"]
という長さ2のリストを作成しています.つまり,["NG","OK"][0] = "NG"
、["NG","OK"][1] = "OK"
です。
真偽値はTrue
のとき1
、False
のとき0
として振る舞うため、input()==input()
がTrue
のときは"OK"、False
のときは"NG"
が表示されます。
bool値についてはこちら
つづいて、 @ryusuke920 さまのほう。
こちらは文字列のスライスを活用しています。
print("NOGK"[input()==input()::2])
"NOGK”
は0文字目と2文字目で"NG"が、1文字目と3文字目で"OK"を作ることができる文字列です。
先ほど紹介した様に、True
は1
、False
は0
として振る舞うため,[input()==input()::2]
の部分は、input()==input()
がTrue
のときは[1::2]
となり、False
のときは[0::2]
となるため、期待した出力が得られます。
競技プログラミングでよく使われるとのことでしたが、私は全くの初見でした。勉強になりました。
終わりに2
記事を投稿することで、いろんな方からコメントをいただきました。ありがとうございます。
発信するために調べたりすることに加えて、みなさんからのコメントも自分の勉強にもなるのでこういった活動は大変おすすめです。みなさんも一度は記事を投稿してみてはいかがでしょうか。