ずんだの菱形アルファベット問題 Python3 & SQL編

もとねた: [ずんだの菱形アルファベット問題 Ruby編](https://qiita.com/tbpgr/items/0567699ba083158804ac)

Python3で、まずは「ふつう」に回答してみました。もとねたでは実行時引数を読み込んでいましたが、以下の解答では標準入力の1行目をnとしています。

```python
def draw(n):
    if n % 2 == 0:
        return 'invalid'
    
    mid = n // 2
    matrix = [['_'] * n for _ in range(n)]
    for x, alphabet in zip(range(n), 'abcdefghijklmnopqrstuvwxyz'):
        y = abs(mid - x) - mid
        matrix[x][mid + y] = matrix[x][mid - y] = alphabet
    
    return '\n'.join(''.join(row) for row in matrix)

if __name__ == '__main__':
    n = int(input())
    print(draw(n))
```

次はPython3の「ふつう」ではない解答、つまり1行野郎で解いたものが以下になります。これも「ふつう」の解答と同様に標準入力の1行目をnとします。

```python
print((lambda n : 'invalid' if n % 2 == 0 else '\n'.join(''.join(ch if y in (abs(n // 2 - x), (n - 1) - abs(n // 2 - x)) else '_' for y in range(n)) for x, ch in zip(range(n), 'abcdefghijklmnopqrstuvwxyz')))(int(input())))
```

たとえばPython3のREPLなどをつかって、上記の1行野郎を実行してみると、想定通りの結果が得られることがわかります。

```
$ python3
Python 3.6.5 (default, Apr  1 2018, 05:46:30)
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print((lambda n : 'invalid' if n % 2 == 0 else '\n'.join(''.join(ch if y in (abs(n // 2 - x), (n - 1) - abs(n // 2 - x)) else '_' for y in range(n)) for x, ch in zip(range(n), 'abcdefghijklmnopqrstuvwxyz')))(int(input())))
5
__a__
_b_b_
c___c
_d_d_
__e__
>>> print((lambda n : 'invalid' if n % 2 == 0 else '\n'.join(''.join(ch if y in (abs(n // 2 - x), (n - 1) - abs(n // 2 - x)) else '_' for y in range(n)) for x, ch in zip(range(n), 'abcdefghijklmnopqrstuvwxyz')))(int(input())))
2
invalid
```

***

Python3だけではさみしいのでSQLでも解答してみました。以下がその解答になります。
入力はバインド変数`:N`として1から25までの整数を与えます。動作確認にはPostgreSQL 10.5を利用していますが、言語依存の機能はさほど利用していないので、多少の読み替えなどはあったとしても、基本的にはほかのDBMSでも動く--はず(´・ω・｀)

```sql
WITH 
RECURSIVE alphabets(x, alphabet, list) AS (
    SELECT -1, NULL, 'abcdefghijklmnopqrstuvwxyz'
  UNION ALL
    SELECT
      x + 1,
      SUBSTRING(list FROM 1 FOR 1),
      SUBSTRING(list FROM 2)
    FROM alphabets
    WHERE LENGTH(list) > 0
),
rows(x, row) AS (
  SELECT
    a1.x, 
    STRING_AGG (
      CASE WHEN a2.x IN (ABS(:N / 2 - a1.x), ABS(:N - 1 - ABS(:N / 2 - a1.x))) THEN a1.alphabet ELSE '_' END, 
      '' 
      ORDER BY a2.x
    )
  FROM alphabets a1 CROSS JOIN alphabets a2
  WHERE 0 <= a1.x AND a1.x < :N
  AND   0 <= a2.x AND a2.x < :N
  GROUP BY a1.x
)
SELECT
  CASE WHEN :N % 2 = 0 THEN 'invalid' ELSE STRING_AGG(row, chr(10) ORDER BY x) END AS answer
FROM rows
```

このSQLをファイル`zunda.sql`に保存し、たとえば以下のようにして実行します。`psql`コマンドでは`-v`オプションを使って、バインド変数に値を設定することができるので、これを利用しています。

```
$ psql -d sandbox -U postgres -v N=24 -f zunda.sql
 answer
---------
 invalid
(1 row)

$ psql -d sandbox -U postgres -v N=25 -f zunda.sql
          answer
---------------------------
 ____________a____________+
 ___________b_b___________+
 __________c___c__________+
 _________d_____d_________+
 ________e_______e________+
 _______f_________f_______+
 ______g___________g______+
 _____h_____________h_____+
 ____i_______________i____+
 ___j_________________j___+
 __k___________________k__+
 _l_____________________l_+
 m_______________________m+
 _n_____________________n_+
 __o___________________o__+
 ___p_________________p___+
 ____q_______________q____+
 _____r_____________r_____+
 ______s___________s______+
 _______t_________t_______+
 ________u_______u________+
 _________v_____v_________+
 __________w___w__________+
 ___________x_x___________+
 ____________y____________
(1 row)
```