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