[pukiwiki]
Pythonでは、リスト内包表記やラムダ(lambda)による関数定義では、式のみを使えます。
なので、「=(イコール、等号)」を使った代入は使えません。
関数型言語でも、Mathematicaでは式の中で代入できるのに~ ちょっと不便。ということで、この制限を回避できないか試した記録です 。
[/pukiwiki]
[pukiwiki]
「せっかくPythonは数値や文字列がイミュータブルなのに」 というツッコミは無しの方向で(汗
*[[条件文の前の代入を取り除く bigstack (西尾泰和のはてなダイアリー):http://d.hatena.ne.jp/nishiohirokazu/20080214/1202981087]]
先行事例。 代入とはちょっと違いますが。
—-
まずは、ちゃんと動くやりかた。
[/pukiwiki]
—-
どうしても式中で代入したい!という貴兄は下記のようなlet関数を定義します。おしまい
def let ( key,value,dict): dict.update( {key:value}) return value
使い方 その1 リスト内包表記
>>> [let("x",i,locals()) for i in range(10)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> x 9 #xに9が代入されてる
使い方 その2 lambda
>>> f=lambda y,d=locals():let("x",y,d) >>> f(101) 101 >>> x 101 >>>
実際は、あまり使いませんが。
[pukiwiki]
*比較演算子の再定義
詳しい解説は省きますが、Pythonでは、変数に格納された数値を直接変更することが出来ません。
-[[ググる:Python イミュータブル]]
なので、「値を保持するためのクラス」を作成します
Pythonでは、アンダースコア二つでくくられた特殊な名前のメソッド(特殊メソッド)により、演算子の動作を再定義できます。
参考
-[[operatorモジュール — 関数形式の標準演算子:http://www.python.jp/doc/release/lib/module-operator.html#l2h-1086]]
-[[Py2.5日本語ドキュメント索引 アンダースコア:http://www.python.jp/doc/2.5/lib/genindex.html#letter-_]]
式中でも使える演算子を一つ潰して、代入に使えるようにしたオブジェクトを作ってみます。
今回は、左シフト演算子(<<) をオーバーロードしました。
[/pukiwiki]
class Var(object): def __init__(self,value=0): self.value=value def __lshift__(self,y): self.value=y return y
実行例
>>> x<<100 100 >>> x.value 100 >>> [x<<i for i in range(10)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> x.value 9 >>>
あんましうれしくない。。。
上記let関数と違ってオブジェクトなので、スコープを気にしなくてもいいのがメリット、かな?
今改めて考えると、値を保持するだけなら、特殊メソッド名使わずとも、 setとか普通のメソッド名にしたほうが無難です。(多分)
めんどくさいですが、ちゃんと四則演算全てを実装してやることも可能かと。
[pukiwiki]
—-
次にダメだった方法
*【それムリ】 +=を使ってみる
x+=a は
x=x+a と同じ意味となります。
だったら、
x+= (-x+a)は、
x=aと同じ意味
になるはずです。やってみます。
[/pukiwiki]
#CPython2.5 >>> x=0 >>> [x+=(-x+i) for i in xrange(100)] SyntaxError: invalid syntax #IronPython2.6RC2 >>> x=0 >>> [x+=(-x+i) for i in xrange(100)] File "<stdin>", line 1 [x+=(-x+i) for i in xrange(100)] ^ SyntaxError: unexpected token '+='
こんなエラーが出ました
+=も使えないのでした。代入を伴う演算子は全て使えないみたいです。残念。
等号を含む比較演算子は使えるみたいですけれども。
[pukiwiki]
*>>=で遅延評価
なんてことをすると[[Haskell>ググる:Haskell]]っぽいかも、というのが、今日のオチ、でした。自分では出来ませんが、、、
考えてみれば、Pythonで書かれた数式処理システム 「[[SymPy>ググる:SymPy]]」も、同じように演算子をオーバーロードして式の表現やSymbolクラスを実装してるはず、なので、また今度ヒマな時にソースを読んでみると面白いのかも
(そこまで辿りつけない気もしますけれども)
—-
オマケ
上記Varクラスに、さらに四則演算を実装した例。
(シフト演算子は省いてあります)
exec文を使ってます。
これだと煩雑なので、全部、基底クラスに委譲する方法とか無いかしらん?
(有りそうな気もするのですが)
[/pukiwiki]
class Var(object): def __init__(self,value=0): self.value=value def _v(self,y): if isinstance(y,type(self)): return y.value else: return y def __lshift__(self,y): self.value=self._v(y) return self def __repr__(self): return str(self.value) oplist="eq,== gt,> lt,< ge,>= le,<=" tmpl="""def __%s__(self,y): return self.value %s self._v(y)""" for i in oplist.split(): t=tuple(i.split(",")) exec (tmpl%t) # oplist="add,+ sub,- mul,* div,/" tmpl="""def __%s__(self,y): return type(self)(self.value %s self._v(y))""" tmpl2="""def __r%s__(self,y): return type(self).spr(self._v(y) %s self.value)""" tmpl3="""def __i%s__(self,y): self.value=self.value %s self._v(y) return self""" tmpl4="""def __ri%s__(self,y): self.value= self._v(y) %s self.value return self""" oplist=oplist.split() for i in oplist: t=tuple(i.split(",")) exec (tmpl%t) exec (tmpl2%t) exec (tmpl3%t) exec (tmpl4%t)