web-dev-qa-db-ja.com

Python Haskell'let 'に相当するものはありますか?

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番目のバージョンは、リスト内包表記ではうまく機能しません。

21
Perseids

一時的なリスト内包表記を使用できます

[(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]
20
huon

最近の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] ]
12
b0fh

そのようなことはない。あなた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)
_
10
user395760

B0fhの回答の複数のfor句は、私がしばらく個人的に使用しているスタイルです。これは、より明確になり、名前空間が一時的な関数で乱雑にならないためです。ただし、速度が問題になる場合は、1つの要素リストを一時的に作成する方が、1つのタプルを作成するよりもかなり時間がかかることを覚えておくことが重要です。

このスレッドのさまざまなソリューションの速度を比較すると、醜いラムダハックが最も遅く、次にネストされたジェネレーター、次にb0fhによるソリューションが続くことがわかりました。ただし、これらはすべて1タプルの勝者によって上回っています。

list2 = [ barcode(productID), metric(size)
          for productID in list
          for (_, size) in (lookup(productID),) ]

これはOPの質問とはあまり関係がないかもしれませんが、ダミーイテレータのリストの代わりに1タプルを使用することで、リスト内包表記を使用したい場合に、明確さを大幅に向上させ、速度を上げることができる場合もあります。

6
dalum

最高の読みやすさを求めたので、ラムダオプションを検討できますが、少しひねりを加えて、引数を初期化します。自分で使用するさまざまなオプションを次に示します。最初に試したものから、現在最も使用しているものまであります。

引数として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でも)では、多くの括弧が最後に積み重なる傾向があります。奇妙なもの '('は簡単に見つけられます。

3
Erik

漠然と比較できるものを得るには、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]
3
senderle

Haskellが何をするかを推測するだけで、これが代替案です。 Pythonで「リスト内包」として知られているものを使用します。

[barcode(productId), metric(size)
    for (productId, (name, size)) in [
        (productId, lookup(productId)) for productId in list_]
]

他の人が示唆しているように、lambda:の使用を含めることができます。

2
vsh

あなたは単にこれを次のように書くことができますが:

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)
1
divs1210
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になります。

1
crizCraig