16行を1行に短縮！初学者向け「文字の一致」簡単コーディング【Paiza RankD】


# この記事の内容
この記事ではPaizaのRankD相当の「文字の一致」のコードを16行から1行に短縮することを通して、基本的なコーディングについて学びます。初学者でも簡単に理解できるようステップバイステップで解説したいと思います。

# 問題
#### 概要
与えられた2つの文字列が一致していればOK、一致していなければNGと出力せよ。
#### 詳細
https://paiza.jp/works/mondai/d_rank_skillcheck_sample/diff_str

# 文字列が一致するとは？
そもそも2つの文字列A,Bが一致しているとは次のように表現することができます。
- 文字列Aと文字列Bの長さが等しい
- 文字列Aと文字列Bのi文字目がすべて等しい

このことを利用して素直に実装すると次のようなコードになります。

[結論（1行コード）だけ見たい方はこちら](#削減5による解答コード2行) 



#### 解答コード（16行）

```Python
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")
```

#### コードの詳細な解説

- 入力の受け取り
```Python
A = input()
B = input()
```
- 2つの文字列が一致しているかどうかを判断するフラグ変数の初期化
Trueで初期化し、文字列が一致していないと判明した段階でFalseに変更する。
```Python
equal_flg = True
```

- 2つの文字列の長さが等しいかチェック
長さが異なる場合は、文字列としても当然異なる
```python
if len(A) == len(B):
    # 略（後述）
else:
    equal_flg = False
```

- 長さが同じ場合に、先頭の文字から順番に一致しているかチェック
一致している場合は、次の文字のチェック
一致していない場合は、文字列として異なるため`equal = False`として、ループを抜ける
ここの`for`が`break`されずに最後までループが回れば文字列として一致していることになる（`equal_flg=True`のまま）

<details><summary>文字列に対する補足</summary>
Pythonの文字列（string）はインデックスを指定して、1文字だけ取り出すことができます。

```python
str = "abcd"
print(str[2]) # c
```

</details>

```python
    for i in range(len(A)):
        if A[i] == B[i]:
            continue
        else:
            equal_flg = False
            break
```

- 結果の出力
```python
if equal_flg:
    print("OK")
else:
    print("NG")
```

#### コードの削減1
`equal_flg`について再考します。
処理をよく見ると、`equal_flg`の役割は、一番最後の結果の出力の際の条件分岐だけです。
その処理を次のように変更します。
- `OK`または`NG`を格納するための変数`ans`を用意する
- `ans = "OK"`として初期化する
- `equal_flg`に関する処理を`ans`で置き換える



具体的な実装は次の様になります。
#### 削減1による解答コード（13行）
```python
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]:`の中に何も処理が書かれていないためです。
```python
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行）
```python:ifの条件を否定
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行）
```python
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行）
```python
ans = "OK"
if input() != input():
    ans = "NG"
print(ans)
```
**2行削減しました！（合計12行）**

*実は、この段階で`if`の条件式を`==`に戻して`OK`と`NG`の代入を逆にしても問題ないのですが、話の流れ上このままにしています。*


#### コードの削減5
今までのコードの削減は処理の流れを変えたり、Pythonの機能を使ったりしましたが、ここからは書き方が変わるだけでやっている処理は同じです。（好みや可読性の違いと思ってください）

実は、Pythonには次のようなif文の書き方があります。（他の言語でも似た様な書き方ができることがあります）
```python
条件式が真のときに返す値 if 条件式 else 条件式が偽のときに返す値
```
これを利用します。
#### 削減5による解答コード（2行）
```python
ans = "NG" if input()!= input() else "OK"
print(ans)
```
という感じなのですが、ここでさらにもう一工夫して
```python
print("NG" if input()!= input() else "OK")
```
とすることができ、1行のコードになりました。

**15行削減しました！**

### 終わりに
これはいわゆるコードゴルフと呼ばれる遊びですが、言語の特徴を理解していないとなかなか短いコードにすることができません。遊びを通して言語への理解が少しでも深まればいいかなと思っています。
ただし、コードは短ければ良いというものではありません。短すぎると可読性が低下する可能性もあります（逆もしかりです）また、最後の1行解答よりも、最初の16行のコードの方が、似た様な別の問題に対処するときに拡張しやすいというメリットがあるかもしれません。

#### 2024.8.20追記

<details><summary>2024-08-19 19:49に @hisuie08 さまからコメントにて指摘をいただきました。</summary>
最後に言及のあった「コードゴルフ」、少々トリッキーですが

```python
print(input()==input()and"OK"or"NG")
```
と、Pythonの短絡評価を用いたコードが別解として存在します。
これであればif elseの6文字をand orの5文字に短縮でき、if直後の空白も含めると合計2文字の短縮が可能ですね。
</details>

Pythonの短絡評価について簡単に説明しておきます。
- 短絡評価の概要
`and`や`or`を使用している式の左から順に評価をしていって，真偽が定まった段階で評価をやめる。
- 例
    - `1==0 and 2==2`は`1==0`が偽なので`2==2`は評価されない
    - `2==2 or 1==0`は`2==2`が真なので`1==0`は評価されない

また，短絡評価では，最後に評価された具体的な値が返り値となります。
```python
P = True and "OK"
print(P) # -> OK

Q = False or "NG"
print(Q) # -> NG
```

なお，`OK`，`NG`はいずれもTrueと判断されます．
文字列の真偽値についてはこちら

https://docs.python.org/ja/3/library/stdtypes.html#truth-value-testing

##### コードゴルフについて
本記事ではコード量をコードの行数で比較していましたが，最後に言及したコードゴルフでは，コードの文字数でコード量を比較します．

https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%BC%E3%83%89%E3%82%B4%E3%83%AB%E3%83%95

#### 2024.8.21さらに追記

@fujitanozomu さま、@ryusuke920 さまからもコメントをいただきました．
せっかくなのでお二人に紹介していただいたコードについても解説します。

まずは　@fujitanozomu さまのほうから。
```python
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値についてはこちら

https://docs.python.org/ja/3/library/stdtypes.html#typebool


つづいて、 @ryusuke920 さまのほう。
こちらは文字列のスライスを活用しています。
```python
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]`となるため、期待した出力が得られます。

競技プログラミングでよく使われるとのことでしたが、私は全くの初見でした。勉強になりました。

### 終わりに２

記事を投稿することで、いろんな方からコメントをいただきました。ありがとうございます。
発信するために調べたりすることに加えて、みなさんからのコメントも自分の勉強にもなるのでこういった活動は大変おすすめです。みなさんも一度は記事を投稿してみてはいかがでしょうか。