5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python:lambdaで代入とかswitchとかしてみる

Last updated at Posted at 2015-05-25

初投稿です。よろしくおねがいします。

RaspberryPiがらみで久しぶりにPythonを触って、自分でやりやすいように追求した結果をメモしときます。

特にいわゆる闇を志向しているわけではありません。
ので何かやっちゃいけないことだったら教えて下さい。

lambdaどうなの?使えるの?

以前は使いどころがいまいち分からなかったlambda。
色々制限があるところが今は何だかむしろ好きかも。
可能なところは全部lambdaで書きたいくらい。

クイックソートの例でいきます。まずdefでは?

def qs( xs ) :
        le = [x for x in xs[1:] if x<=xs[0] ]
        gt = [x for x in xs[1:] if x>xs[0] ]
        if xs == []:
                return []
        else:
                return qs( le ) + [ xs[0] ] + qs( gt )

こんな感じでしょうか。

代入できる?

lambdaで普通に書くと、

qs=(lambda xs :
	[] if xs == [] else
    qs( [x for x in xs[1:] if x<=xs[0] ] ) + [ xs[0] ] 
    + qs( [x for x in xs[1:] if x>xs[0] ] )
)

悪くはないけど、長くてごちゃごちゃしてる感あり。これだったらdefするかなあ。やっぱり。
途中での変数への代入はできないけど、名前に束縛して定義しておくことは出来るみたい。

qs=(lambda xs :
    (lambda 
    	le = [x for x in xs[1:] if x<=xs[0] ] ,
    	gt = [x for x in xs[1:] if x>xs[0] ] :
            [] if xs == [] else
            qs( le ) + [ xs[0] ] + qs( gt )
    )()
)

すっきりしてていいんじゃない?returnとかなくて僕好みなんですけど...
内側にもう一つlambda式を作ってデフォルト引数のまま実行させてます。
ってことは設定したデフォルト引数はそのまま変数として使えるってことですよね?

内側のlambda式からは自分より外側の引数や変数が使えます。
lambda x=1,y=x: のようにすると、yは隣のx=1でなく自分より外側にxを探しに行ってしまいます。
関係ない同士の引数は同じlambda式内に。参照関係がある場合は段を変えて。
段数は必要に応じて増減します。

ちなみに一番外側の引数には定数以外の動的なデフォルト引数を設定しないほうがいいようです。最初に生成した時のデフォルト値が記録されてしまって、大概思った通りの動作になりません。

switchしたり、逐次実行したり...

結局、lambdaでやりたい(やれる)ことって

1.引数から導かれる条件で分岐して
2.不純な?動作(入出力とか表示とか)を逐次実行しつつ
3.純粋な計算をして値を返す。

これにつきる気がします。
分岐が多くなると~if~else~しか使えないのがちょっとつらい。
条件を先に、動作を後に書きたい!

  • 何かswitch的なもの(Haskellのガードみたいなの)
  • その中で逐次実行しつつ値を返すもの

が欲しいなと思って作ってみました。といってもそんなにテクはないので、見た目ごまかし系でアプローチ。

まず、準備。

box=lambda *x:x
unbox=lambda x:x[-1]
    
do=box             
switch=unbox

使う時は

switch(
	(条件1)    and do(実行式1a,実行式1b,...)
	or (条件2) and do(実行式2a,実行式2b,...)
	...
	or            do(最後の実行式a,最後の実行式b,...)
)

これで条件で分岐して、do()で式を順番に評価しつつ結果をタプルとしてswitch()に渡し、switch()はタプルの最後の要素を返します。
最後のdo()にはすべての条件が偽になった時の式を入れます。

タプルに入れたり出したりするのは、実行式の返り値がand/orの制御に影響しないようにするためです。

switchとdoには特に意味はありません。ヒト的には何となくそれっぽいかなと思って変えてみました。
名前から想像する機能はありません。

  • 分岐の機能はand/orの短絡評価で、
  • それが確実に動くようにboxして、
  • 同時に関数boxの実行時に引数が評価されるのを利用して逐次実行、
  • 返り値を取り出すためにunbox
    各要素の合わせ技で欲しい機能を実現しています。
    ただ、その辺にswitchとかdoとか書いてあったら読みやすいし書きやすいかな、ということです。guardとかcondとか使ったらかっこいいかも?モテる?

クイックソートの例では


qs=(lambda xs :
    (lambda 
        le = [x for x in xs[1:] if x<=xs[0] ] , 
        gt = [x for x in xs[1:] if x>xs[0] ] :
        switch(
            xs == [] and do( [] )  
            or do( qs( le ) + [ xs[0] ] + qs( gt ) )
        )
    )()
)

ちょっと字数が増えたけど、何となく意味は伝わりそう。
自分でも間違えなさそう。

ちなみに、同じことを普通に書くと

qs=(lambda xs :
    (lambda 
        le = [x for x in xs[1:] if x<=xs[0] ] , 
        gt = [x for x in xs[1:] if x>xs[0] ] :
        (
            xs == [] and ( [], )  
            or ( qs( le ) + [ xs[0] ] + qs( gt ) ,)
        )[-1]
    )()
)

これはこれですっきりしてる。漢だね。
でも、何してるかよくわからないし、書くとき[-1]とかコンマとか忘れそう。

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?