Есть ли эквивалент Python для Haskell 'let' - программирование

Есть ли эквивалент Python для Haskell 'let'

Есть ли эквивалент Python выражения H letell '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))

Вторая версия не очень хорошо работает со списками.

4b9b3361

Ответ 1

Вы можете использовать временное понимание списка

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

Ответ 2

Нет такой вещи. Вы можете эмулировать его так же, как let отсылается к исчислению лямбда (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)

Ответ 3

Последние версии python допускают множественные выражения в выражении генератора, поэтому теперь вы можете сделать что-то вроде:

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] ]

Ответ 4

Несколько для предложений в b0fh ответе - это стиль, который я лично использовал некоторое время, поскольку я считаю, что он обеспечивает большую ясность и не загромождает пространство имен временными функциями. Однако, если скорость является проблемой, важно помнить, что временное построение одного списка элементов занимает значительно больше времени, чем построение одного кортежа.

Сравнивая скорость различных решений в этом потоке, я обнаружил, что уродливый лямбда-хак является самым медленным, за которым следуют вложенные генераторы, а затем решение b0fh. Однако все они были превзойдены победителем одного набора:

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

Это может быть не так актуально для вопроса ОП, но есть и другие случаи, когда ясность может быть значительно увеличена и скорость достигается в случаях, когда можно было бы использовать понимание списка, используя однокортежи вместо списков для манекена итераторы.

Ответ 5

Чтобы получить что-то смутное сопоставление, вам нужно будет сделать два понимания или карты или определить новую функцию. Один из подходов, который еще не был предложен, состоит в том, чтобы разбить его на две строки. Я считаю, что это несколько читаемо; хотя, вероятно, определение вашей собственной функции - это правильный путь:

pids_names_sizes = (pid, lookup(pid) for pid in list1)
list2 = [(barcode(pid), metric(size)) for pid, (name, size) in pids_names_sizes]

Ответ 6

Только гадать, что делает Хаскелл, вот альтернатива. Он использует то, что известно в Python как "понимание списка".

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

Вы можете включить использование lambda:, как предложили другие.

Ответ 7

Хотя вы можете просто написать это как:

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)

Ответ 8

Поскольку вы запросили лучшую читаемость, вы можете рассмотреть вариант лямбда, но с небольшим завихрением: инициализировать аргументы. Вот несколько вариантов, которые я использую сам, начиная с первого, который я пробовал и заканчивая тем, который я использую сейчас.

Предположим, что у нас есть функция (не показана), которая получает data_structure в качестве аргумента, и вам нужно повторно получить от нее x.

Первая попытка (согласно ответу 2012 года от huon):

(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) многие скобки, как правило, накапливаются в конце. "Нечетный" ( "легко заметить".