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