Python Haskell'let '式に相当するものがありますか?これにより、次のようなものを書くことができます:
list2 = [let (name,size)=lookup(productId) in (barcode(productId),metric(size))
for productId in list]
そうでない場合、最も読みやすい代替手段は何でしょうか?
Let構文を明確にするために追加されました:
x = let (name,size)=lookup(productId) in (barcode(productId),metric(size))
と同等です
(name,size) = lookup(productId)
x = (barcode(productId),metric(size))
ただし、2番目のバージョンは、リスト内包表記ではうまく機能しません。
一時的なリスト内包表記を使用できます
[(barcode(productId), metric(size)) for name, size in [lookup(productId)]][0]
または、同等に、ジェネレータ式
next((barcode(productId), metric(size)) for name, size in [lookup(productId)])
しかし、それらは両方ともかなり恐ろしいです。
別の(恐ろしい)方法は、すぐに呼び出す一時的なラムダを介したものです
(lambda (name, size): (barcode(productId), metric(size)))(lookup(productId))
推奨される「Pythonic」の方法は、次のような関数を定義することだと思います。
def barcode_metric(productId):
name, size = lookup(productId)
return barcode(productId), metric(size)
list2 = [barcode_metric(productId) for productId in list]
最近のpythonバージョンでは、ジェネレータ式で複数のfor句を使用できるため、次のようなことができます。
list2 = [ barcode(productID), metric(size)
for productID in list
for (name,size) in (lookup(productID),) ]
これはHaskellが提供するものにも似ています:
list2 = [ (barcode productID, metric size)
| productID <- list
, let (name,size) = lookup productID ]
と表記的に同等
list2 = [ (barcode productID, metric size)
| productID <- list
, (name,size) <- [lookup productID] ]
そのようなことはない。あなたcouldlet
がラムダ計算に脱糖されるのと同じ方法でエミュレートします(_let x = foo in bar
_ <=> _(\x -> bar) (foo)
_)。
最も読みやすい代替手段は、状況によって異なります。あなたの特定の例として、私は[barcode(productId), metric(size) for productId, (_, size) in Zip(productIds, map(lookup, productIds))]
(本当に醜いです。productId
も必要ない方が簡単です。map
を使用できます)または明示的なfor
ループ(発生器):
_def barcodes_and_metrics(productIds):
for productId in productIds:
_, size = lookup(productId)
yield barcode(productId), metric(size)
_
B0fhの回答の複数のfor句は、私がしばらく個人的に使用しているスタイルです。これは、より明確になり、名前空間が一時的な関数で乱雑にならないためです。ただし、速度が問題になる場合は、1つの要素リストを一時的に作成する方が、1つのタプルを作成するよりもかなり時間がかかることを覚えておくことが重要です。
このスレッドのさまざまなソリューションの速度を比較すると、醜いラムダハックが最も遅く、次にネストされたジェネレーター、次にb0fhによるソリューションが続くことがわかりました。ただし、これらはすべて1タプルの勝者によって上回っています。
list2 = [ barcode(productID), metric(size)
for productID in list
for (_, size) in (lookup(productID),) ]
これはOPの質問とはあまり関係がないかもしれませんが、ダミーイテレータのリストの代わりに1タプルを使用することで、リスト内包表記を使用したい場合に、明確さを大幅に向上させ、速度を上げることができる場合もあります。
最高の読みやすさを求めたので、ラムダオプションを検討できますが、少しひねりを加えて、引数を初期化します。自分で使用するさまざまなオプションを次に示します。最初に試したものから、現在最も使用しているものまであります。
引数としてdata_structure
を取得する関数(図には示されていません)があり、そこからx
を繰り返し取得する必要があるとします。
最初の試み(huonからの2012年の回答による):
(lambda x:
x * x + 42 * x)
(data_structure['a']['b'])
複数の記号があると、これは読みにくくなるので、次に試しました。
(lambda x, y:
x * x + 42 * x + y)
(x = data_structure['a']['b'],
y = 16)
それは象徴的な名前を繰り返すので、それはまだあまり読みにくいです。だから私は試しました:
(lambda x = data_structure['a']['b'],
y = 16:
x * x + 42 * x + y)()
これはほとんど「let」式として読み取られます。もちろん、割り当ての配置とフォーマットはあなた次第です。
このイディオムは、開始 '('と終了 '()'によって簡単に認識されます。
関数式(Pythonでも)では、多くの括弧が最後に積み重なる傾向があります。奇妙なもの '('は簡単に見つけられます。
漠然と比較できるものを得るには、2つの理解またはマップを実行するか、新しい関数を定義する必要があります。まだ提案されていないアプローチの1つは、そのように2行に分割することです。これはある程度読みやすいと思います。おそらくあなた自身の関数を定義することが正しい方法ですが:
pids_names_sizes = (pid, lookup(pid) for pid in list1)
list2 = [(barcode(pid), metric(size)) for pid, (name, size) in pids_names_sizes]
Haskellが何をするかを推測するだけで、これが代替案です。 Pythonで「リスト内包」として知られているものを使用します。
[barcode(productId), metric(size)
for (productId, (name, size)) in [
(productId, lookup(productId)) for productId in list_]
]
他の人が示唆しているように、lambda:
の使用を含めることができます。
あなたは単にこれを次のように書くことができますが:
list2 = [(barcode(pid), metric(lookup(pid)[1]))
for pid in list]
LET
を自分で定義して次を取得できます。
list2 = [LET(('size', lookup(pid)[1]),
lambda o: (barcode(pid), metric(o.size)))
for pid in list]
あるいは:
list2 = map(lambda pid: LET(('name_size', lookup(pid),
'size', lambda o: o.name_size[1]),
lambda o: (barcode(pid), metric(o.size))),
list)
次のように:
import types
def _obj():
return lambda: None
def LET(bindings, body, env=None):
'''Introduce local bindings.
ex: LET(('a', 1,
'b', 2),
lambda o: [o.a, o.b])
gives: [1, 2]
Bindings down the chain can depend on
the ones above them through a lambda.
ex: LET(('a', 1,
'b', lambda o: o.a + 1),
lambda o: o.b)
gives: 2
'''
if len(bindings) == 0:
return body(env)
env = env or _obj()
k, v = bindings[:2]
if isinstance(v, types.FunctionType):
v = v(env)
setattr(env, k, v)
return LET(bindings[2:], body, env)
class let:
def __init__(self, var):
self.x = var
def __enter__(self):
return self.x
def __exit__(self, type, value, traceback):
pass
with let(os.path) as p:
print(p)
ただし、これは実質的にp = os.path
と同じです。これは、p
のスコープがwithブロックに限定されていないためです。それを達成するには、
class let:
def __init__(self, var):
self.value = var
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
del var.value
var.value = None
with let(os.path) as var:
print(var.value) # same as print(os.path)
print(var.value) # same as print(None)
ここで、var.value
はwithブロックの外側ではNone
になりますが、その中ではos.path
になります。