У меня есть упражнение на Python следующим образом:
многочлен задается в виде набора коэффициентов, так что степени определяются индексами, например: (9,7,5) означает 9 + 7 * x + 5 * x ^ 2
написать функцию для вычисления ее значения для данного х
Так как в последнее время я занимаюсь функциональным программированием, я написал
def evaluate1(poly, x):
coeff = 0
power = 1
return reduce(lambda accu,pair : accu + pair[coeff] * x**pair[power],
map(lambda x,y:(x,y), poly, range(len(poly))),
0)
который я считаю нечитаемым, поэтому я написал
def evaluate2(poly, x):
power = 0
result = 1
return reduce(lambda accu,coeff : (accu[power]+1, accu[result] + coeff * x**accu[power]),
poly,
(0,0)
)[result]
по крайней мере так же нечитабельно, поэтому я написал
def evaluate3(poly, x):
return poly[0]+x*evaluate(poly[1:],x) if len(poly)>0 else 0
который может быть менее эффективным (правка: я был неправ!), поскольку он использует множество умножений вместо возведения в степень, в принципе, мне здесь наплевать на измерения (правка: как глупо с моей стороны! Измерения указали бы на мое заблуждение!) и все еще не так читабельно (возможно), как итеративное решение:
def evaluate4(poly, x):
result = 0
for i in range(0,len(poly)):
result += poly[i] * x**i
return result
Есть ли чисто функциональное решение, столь же удобочитаемое, как императив, и близкое к нему по эффективности?
По общему признанию, изменение представления помогло бы, но это было дано упражнением.
Может быть Haskell или Lisp, а не только Python.
for
, например, не использование циклов), является плохой целью, к которой нужно стремиться в Python. Разумное повторное связывание переменных и отсутствие изменения объектов дает вам практически все преимущества и делает код бесконечно более читабельным. Поскольку числовые объекты являются неизменяемыми и связывают только два локальных имени, ваше «императивное» решение лучше реализует преимущества функционального программирования, чем любой «строго чистый» код Python.lambda
начинаете использовать его , по сравнению с языками с более легкой анонимной синтаксической функцией. Частично это, вероятно, способствует «нечистой» внешности.Ответы:
Метод Хорнера, вероятно, более вычислительно эффективен, как указывает @delnan, но я бы назвал это довольно читабельным в Python для решения возведения в степень:
источник
sum(coeff * X**power for power, coeff in enumerate(poly))
map
иfilter
. Можно также думать о нем как о петле for определенной формы, но петли этой формы эквивалентны вышеупомянутому функциональному комбинатору.Многие функциональные языки имеют реализации mapi, которые позволяют вам иметь индекс, сплетенный через карту. Объедините это с суммой, и вы получите следующее в F #:
источник
map
работает, написать свой собственный будет довольно просто.Я не понимаю, как ваш код соотносится с областью задачи, которую вы определили, поэтому я приведу свою версию того, что ваш код игнорирует область проблемы (на основе написанного вами императивного кода).
Довольно читаемый haskell (этот подход может быть легко переведен на любой язык FP, который имеет деструктуризацию списка и получается чистым и читаемым):
Иногда такой простой наивный подход в Haskell чище, чем более лаконичный подход к людям, менее привыкшим к FP.
Более четкий императивный подход, который все еще полностью чист:
Бонус ко второму подходу заключается в том, что в монаде ST он будет работать очень хорошо.
Хотя, чтобы быть уверенным, наиболее вероятной реальной реализацией от Haskeller будет zipwith, упомянутый в другом ответе выше.
zipWith
Это очень типичный подход, и я верю, что Python может имитировать подход комбинирования функций и индексатора, который можно отобразить.источник
Если у вас просто есть (фиксированный) кортеж, почему бы не сделать это (в Haskell):
Если вместо этого у вас есть список коэффициентов, вы можете использовать:
или с уменьшением, как у вас было:
источник
tuple
имел в виду набор значений фиксированного размера, каждый из которых может иметь разные типы, которые нельзя добавлять или удалять. Я никогда не понимал, почему они нужны динамическому языку со списками, который принимает гетерогенные входные данные.Существует общий набор шагов, которые вы можете использовать для улучшения читаемости функциональных алгоритмов:
evaluateTerm
длинного лямбда-выражения. То, что вы можете использовать лямбду, не обязательно означает, что вы должны это делать .enumerate
илиzipWith
.источник