需要のないページ

プログラミングや趣味や。

クソみたいなPythonワンライナーを書いてみた

前書き

あるときふと思った。クソコードを書きたい。

コードと動作

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


    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())))     
    

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


    >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]   
    

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

一応解説

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


    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()))
    )  
    

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


    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()))
    )
    

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


    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)    
    )
    

普通に書くなら

ラムダ式での再帰のために、第一引数に自身を取る冗長な構造にしている。
こちらのページが非常に参考になった。

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


    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)
    

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


    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)    
    

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

その後の足掻き


    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()))
        )
    )
    

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


    >>> 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]
    

なんとも恐ろしい。

さらに改造

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


    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
                # 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を除く)が1つだったら素数
        ) == 1])
        # 求める素数の上限
        (int(input()))
    ) 
    

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


    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で落ちる。
末尾再帰最適化で解消できるはずだが、まあそこまではしなくてよいだろう。

一応参考までに。
さすがにこれも込みでワンライナーにするのは骨だ。

 

晒してみた