pythonで一行プログラミングしてみよう

## 初めに
こんにちは、六角レンチです。（唐突）
趣味レベルで適当にプログラミングしてます
最近記事を見ていたら**pythonは一行で書くことができない～**みたいな感じの物を見つけていや色々使えば行けるぞって思ったので適当に書いてみます
## 環境
OS:windows10
python:3.9.6
## 複数行の出力
初めに言った記事の内容では出力が
```
1
2
3
...
9
10
```
みたいなプログラムを書こうとすると
```py
for i in range(10):
    print(i+1)
```
で二行必要～って書いてありました

本当にそうでしょうか？
### リスト内包表記
みなさんリスト内包表記って知ってますか？
こんな感じのやつです
`[char for char in range(10)]`
実はこれを使って上のプログラムを一行で書くことができます
それがこれです
```py
[print(i+1) for i in range(10)]
```
実行してみると確かに同じ出力になってます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3594202/6f69d924-9e58-71e4-36a8-d890e674305e.png)
ではなぜ出力できるんでしょうか？
### 実は関数を実行できる
実はリスト内包表記は関数を実行できます。
そしてprint()は一応関数なのでprint(i+1)を10回実行した結果をリストに格納するというリスト内包表記で実行できるのです（画像のリストのNoneはprintの返り値）

このリスト内包表記を使ってもっといろんなプログラムを**一行で**実行したくありませんか？
例えば...そうですね、FizzBuzzなんてどうでしょうか
## 一行FizzBuzzプログラム
FizzBuzzはプログラミングしたことがあるなら一回は作ったことのあるプログラムだと思います。
まずは試しに普通に1~100までのFizzBuzzをするプログラムを書いてみます
```py
def fizzbuzz(number):
    if number % 3 == 0 and number % 5 == 0:
        print("FizzBuzz")
    elif number % 3 == 0:
        print("Fizz")
    elif number % 5 == 0:
        print("Buzz")
    else:
        print(number)

for i in range(100):
    fizzbuzz(i+1)
```
...長いですね。
まず最初にfizzbuzz関数を取り除いてfor文だけにしてみましょう
```py
for number in range(1, 101):
    if number % 3 == 0 and number % 5 == 0:
        print("FizzBuzz")
    elif number % 3 == 0:
        print("Fizz")
    elif number % 5 == 0:
        print("Buzz")
    else:
        print(number)
```
まだまだ長いですね

さて、ここで一行で書くときに一番厄介になりそうなのがいます。**if文です**。
どうやって一行で書くんでしょうか？
### 三項演算子
ここで使うのが**三項演算子**です(名前かっこいい)
使い方は
`真の時 if 条件式 else 偽の時`

では書いてみます
```py
for number in range(1, 101):
    print("FizzBuzz") if number % 3 == 0 and number % 5 == 0 else print("Fizz") if number % 3 == 0 else print("Buzz") if number % 5 == 0 else print(number)
```
一気に横に長くなりましたね
まぁ一行で書くので当たり前ですけど
あとは一つ前でやったリスト内包表記に入れてあげるだけです。
```py
[print("FizzBuzz") if number % 3 == 0 and number % 5 == 0 else print("Fizz") if number % 3 == 0 else print("Buzz") if number % 5 == 0 else print(number) for number in range(1, 101)]
```

一行で書くことができました(すごい横に長いけど)
ちゃんと動きます(出力は長いので途中まで)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3594202/6bd98cc8-0a70-05ed-bc40-6c92ccd56546.png)
## もっとやりたい
一行でFizzBuzzを書くことができましたけどこんなのじゃまだ物足りません。
もっと行きましょう

次はフィボナッチ数列を求めてみます
## 一行フィボナッチ数列プログラム
まずはフィボナッチ数列のプログラムを書きたいですが面倒なのでchatgpt君に作ってもらいます(おい)
作ってもらったのがこちらです
```py
def fibonacci(n):
    fib_sequence = [0, 1]

    while len(fib_sequence) < n:
        fib_sequence.append(fib_sequence[-1] + fib_sequence[-2])

    return fib_sequence[:n]

# フィボナッチ数列の長さを指定
n = int(input("フィボナッチ数列の長さを入力してください: "))

result = fibonacci(n)
print(f"フィボナッチ数列: {result}")
```
実行してみるとちゃんと動いてそうです
```
フィボナッチ数列の長さを入力してください: 10
フィボナッチ数列: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
```
まずはwhile文のところをfor文に変えます
あとコメントも邪魔なので消します
下の方もまとめておきましょう
```py
def fibonacci(n):
    fib_sequence = [0, 1]

    for _ in range(n-2):
        fib_sequence.append(fib_sequence[-1] + fib_sequence[-2])

    return fib_sequence[:n]

print(f'フィボナッチ数列: {fibonacci(int(input("フィボナッチ数列の長さを入力してください: ")))}')
```
さて、ここまでやりましたけど一行でやるとなると関数であるfibonacciをどうやって一行にしようか迷います
ここは**無名関数**を使ってみましょう(名前かっこいい)
### 無名関数(lambda式)
今回使うのはlambda式こと無名関数です。
使い方は
`lambda 引数 : 返り値`

def文で書いた場合
```py
def 名前(引数):
    return 返り値
```
これとおんなじです
名前がないので無名関数です

なぜlambda**式**なのかというと大体式として使われるからです(sortとかmapとか、自分は使ったことないけど)
ちなみに名前を付ける(変数に代入する)とPEP8に怒られます

では書いてみましょう
```py
(lambda n, fib_sequence = [0, 1] : (lambda _ = [fib_sequence.append(fib_sequence[-1] + fib_sequence[-2]) for _ in range(n-2)] : fib_sequence[:n])())
```
...なんですかねこれ
一個ずつばらしてみていきましょう
```py
(lambda 
 n,
 fib_sequence = [0, 1]
 : (lambda 
    _ = [fib_sequence.append(fib_sequence[-1] + fib_sequence[-2]) for _ in range(n-2)]
    : fib_sequence[:n])
    ()
    )
```
二つの無名関数を使っています
実は無名関数も引数の部分でデフォルト値を書けるのでそこでリスト内包表記を使ってappendするという操作をしてます

最初の無名関数でフィボナッチ数列の長さになるnを受け取り、ついでに変数のfib_sequenceを作っておきます(無名関数は変数の宣言が無理なので引数を変数と考えて作る)

次の無名関数では引数を利用してfib_sequenceにフィボナッチ数列を作っていきます
最初に説明したリスト内包表記です、appendも関数なのでもちろん実行できます。(返り値はNone)
そして最後にfib_returnを長さで切って返します(長さで切らないと長さが0と1の時に出力が違ってしまう)

コメントを書くとこんな感じですかね
```py
(lambda #最初の無名関数
 n, #フィボナッチ数列の長さ
 fib_sequence = [0, 1] #フィボナッチ数列になる変数
 : (lambda #二つ目の無名関数
    _ = [fib_sequence.append(fib_sequence[-1] + fib_sequence[-2]) for _ in range(n-2)] #変数にフィボナッチ数列を作っていく
    : fib_sequence[:n]) #フィボナッチ数列を長さで切って返す
    () #二つ目の無名関数を最初の無名関数が実行する
    )
```
最後にこれらを合体して...
完成しました
```py
print(f'フィボナッチ数列: {(lambda n, fib_sequence = [0, 1] : (lambda  _ = [fib_sequence.append(fib_sequence[-1] + fib_sequence[-2]) for _ in range(n-2)] : fib_sequence[:n])())(int(input("フィボナッチ数列の長さを入力してください: ")))}')
```

一行で書けました
実行してみましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3594202/b84e05a8-fabb-b8a0-c9b7-5e68cca8974a.png)
ちゃんと動いてそうです
## 一行プログラミングの限界？
ここまでいろいろなプログラムを一行で書きましたが残念ながら一行で書けないプログラムもあります。(自分の知識不足かもしれないけど)
それはライブラリを使うプログラムです
例えば
```py
import random

for _ in range(random.randint(1,6)):
    print(random.randint(1,6))
```
このようなプログラムを小さくしても
```py
import random
[print(random.randint(1,6)) for _ in range(random.randint(1,6))]
```
これが限界です。import文を一行に入れることができないです。
<br><br><br><br>
と思っていた時期が私にもありました。
## 限界なんてなかった
記事を書いてる途中自分以外にもこういうことしてる人いるだろうと思ったらとんでもない物を見つけてしまいました。
__import__です。

pythonドキュメントより引用
https://docs.python.org/ja/3/library/functions.html?ref=trap.jp#import__
>この関数は import 文により呼び出されます

つまりimport文です。
なので上で限界と言っていたプログラムは次のように書けます
```py
(lambda random = __import__("random",globals(),locals(),[],0) : [print(random.randint(1,6)) for _ in range(random.randint(1,6))])()
```
ちゃんと動いてそうです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3594202/add1d4f8-5f38-bb32-9975-bd07bbf8cd37.png)

恐ろしい...
## まとめ
最初は一行でプログラミングできるけど無理なのもあるよ～みたいな感じで書こうと思ってたんですけど__import__とかいう恐ろしいものを見つけてしまいました...
なんかこれ以上は沼に入りそうなのでやめておきます。