素因数を出力するワンライナーを書いてみた

# 前書き
あるときふと思った。**クソコードを書きたい。**
*この記事は、細々とやっているブログからの転載です。*

# コードと動作
コードは次の通り。素因数を表示するものだ。
一応Python3だが、print関数をprint文に置き換えればPython2でも動作する。

``` python:
print((lambda f: (lambda n: f(f, [], n, 2)))(lambda f, r, n, i: r if n == 1 else (r.append(i) or f(f, r, n/i, i)) if n % i == 0 else f(f, r, n, i+1))(int(input())))
```

実行とその結果は次のようである。

``` cmd
>python -c "print((lambda f: (lambda n: f(f, [], n, 2)))(lambda f, r, n, i: r if n == 1 else (r.append(i) or f(f, r, n/i, i)) if n % i == 0 else f(f, r, n, i+1))(int(input())))"     
100
[2, 2, 5, 5]   
```

我ながら満足のクソっぷりである。
改善したい点を挙げるとしたら、リストを使いまわしているところだろうか。
ラムダ式を上手くジェネレータのように活用できないだろうか。

# 一応解説
解説というか、見やすく分解しただけだが。

``` python:
print(
    # 再帰可能にする工夫と、高階関数としての役割
    (lambda func: 
        (lambda num:
            func(func, [], num, 2)
        )
    )
    # 素因数分解をつかさどる部分
    (lambda func, result, num, i:
        # 終了条件と返り値
        result if num == 1
        # i が num の約数のとき 
        else (result.append(i) or func(func, result, num/i, i)) if num % i == 0    
        # i が num の約数でないとき
        else                      func(func, result, num, i+1)
    )
    # 実際に関数に与える引数
    (int(input()))
)  
```

ワンラインの拘りなしにこれを書くとこのようになる。

``` python:
def higher_order(func):
    return lambda num: func(func, [], num, 2)    

@higher_order
def ret_factor(func, result, num, i):
    if num == 1:
        return result
    
    if num % i == 0:
        result.append(i)
        return func(func, result, num/i, i)
    else:
        return func(func, result, num, i+1)
    
print(
    ret_factor(int(input()))
)
```

そもそもラムダ式がわからん。デコレータも知らん。という方はこちら。

``` python
def ret_factor(func, result, num, i):
    if num == 1:
        return result
 
    if num % i == 0:
        result.append(i)
        return func(func, result, num/i, i)
    else:
        return func(func, result, num, i+1)
    
print(
    ret_factor(func=ret_factor, result=[], num=int(input()), i=2)    
)
```

# 普通に書くなら
ラムダ式での再帰のために、第一引数に自身を取る冗長な構造にしている。
[Pythonでのワンライナー](https://qiita.com/gyu-don/items/1c200d4e9fc6e22d3c7b#%E5%86%8D%E5%B8%B0)が非常に参考になった。

普通に書くなら、次のようになる。

``` python
def ret_factor(result, num, i):
    if num == 1:
        return result
    
    if num % i == 0:
        result.append(i)
        return ret_factor(result, num/i, i)    
    else:
        return ret_factor(result, num, i+1)
```

また、リストに値を格納して最後に返すのは、大抵無駄だ。
このようなときには、ジェネレータを使うべきである。

``` python
def ret_factor(num, i):
    if num == 1:
        return
   
    if num % i == 0:
        yield i
        yield from ret_factor(num/i, i)
    else:
        yield from ret_factor(num, i+1)    
```

本当はこれをワンラインにしたいのだが...
いかんせん終了時の処理を上手く表現できないのだ。

# その後の足掻き

``` python
print(
    list(
        # 再帰可能にする工夫と、高階関数としての役割
        (lambda func: 
            (lambda num:
                func(func, num, 2)
            )
        )
        # 素因数分解をつかさどる部分
        (lambda func, num, i:
            # 終了条件
            None if num == 1
            # i が num の約数のとき 
            else ((yield i), (yield from func(func, num/i, i))) if num % i == 0    
            # i が num の約数でないとき
            else             (yield from func(func, num, i+1))
        )
        # 実際に関数に与える引数
        (int(input()))
    )
)
```

このようにしたら出来た。達成感より先に気持ち悪さがあるが。
これはつまり、次のように書けるということなのだ...

``` cmd:
>>> def func(num):
...     ((yield num), (yield from func(num-1))) if num > 0 else None    
...
>>> list(func(10))
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
```

なんとも恐ろしい。

# さらに改造
例えば、次のように改造できる。

``` python:
print(
    (lambda max: [m for m in range(2, max) if
        # 約数(1を除く)が1つだったら素数
        len(
            # 再帰可能にする工夫と、高階関数としての役割
            (lambda func: 
                (lambda num:
                    func(func, [], num, 2)
                )
            )
            # 素因数分解をつかさどる部分
            (lambda func, result, num, i:
                # 終了条件と返り値
                result if num == 1
                # i が num の約数のとき 
                else (result.append(i) or func(func, result, num/i, i)) if num % i == 0    
                # i が num の約数でないとき
                else                      func(func, result, num, i+1)
            )
            # 実際に関数に与える引数
            (m)
        ) == 1]
    )
    # 求める素数の上限
    (int(input()))
)
```

改行と無駄な空白を潰すとこんな感じ。

``` python
print((lambda max: [m for m in range(2, max) if len((lambda func: (lambda num: func(func, [], num, 2)))(lambda func, result, num, i: result if num == 1 else (result.append(i) or func(func, result, num/i, i)) if num % i == 0 else func(func, result, num, i+1))(m)) == 1])(int(input())))
```

入力された数値未満の素数リストを出力するワンライナーだ。
しかし、1000ほどの大きな値を入れるとRecursionErrorで落ちる。
末尾再帰最適化で解消できるはずだが、まあそこまではしなくてよいだろう。

一応参考までに。[Pythonのクロージャで末尾再帰最適化をする。](http://tanihito.hatenablog.com/entry/20110119/1295459297)
さすがにこれも込みでワンライナーにするのは骨だ。