Причина, по которой это «работает» без копирования, заключается в том, что когда вы делаете рекурсивный вызов, вы ничего не возвращаете из него. Каждый рекурсивный вызов повторно фильтрует исходные данные, и как только вы вернулись из всего, исходные данные были полностью повторно отфильтрованы. Если вы делаете копии, то создаются последовательно более отфильтрованные копии, но исходные данные не изменяются, и нет доступа к более отфильтрованным копиям из вызывающей области.
Это исправлено return
результатом рекурсивного вызова. В момент, когда вы делаете начальный вызов, вы должны захватить возвращаемое значение (и, возможно, переназначить его исходной переменной). Конечно, чтобы все работало правильно, вам нужно возвращать одно и то же везде, куда вы возвращаете. Поэтому вы должны вернуть кортеж (candid_input, constraint_input), чтобы у вас были эти данные, и позволить сайту вызова интерпретировать результат . Ваш код запутан; ответственность не разделена должным образом. Здесь есть две задачи: фильтрация данных и определение значения отфильтрованных данных.
Когда вы пишете рекурсивный код, вы обычно хотите, чтобы он был в функциональном стиле. Это означает: не изменяйте вещи на месте, а создавайте измененные версии. Для последовательности и аккуратности вы должны делать это даже для подэтапов.
def OT_eval(candidates_input, constraint_input):
# It's much cleaner to handle base cases for recursion **first**.
if not (constraint_input and candidates_input):
# We ran out of one or the other. BTW, your original code doesn't
# consider the case of an overly constrained situation.
return (candidates_input, constraint_input)
constraint = constraint_input[0]
# At first I was going to replace the call to `.sort()` by using
# the free function `sorted` to maintain the "functional" theme.
# However, you don't really need to sort the list, as far as I can
# tell; you just need to find the minimum and use it as a basis for
# comparison.
violations = [
(len(re.findall(constraint, candidate)), candidate)
for candidate in candidates_input
]
# Now we create "all candidates with more than the minimum violations"
minimal_violations = min(violations)[0]
violators = [
candidate
for violation_count, candidate in violations
if violation_count > minimal_violations
]
# And hence "all candidates without that many violations"
good_candidates = [
candidate
for candidate in input_candidates
if candidate not in violators
]
# And now we can recurse.
return OT_eval(good_candidates, constraint_input[1:])
# Then, outside, we do the actual result processing and output:
final_candidates, final_constraints = OT_eval(candidates, constraints)
if final_constraints:
print "No candidates survived the selection process."
elif len(final_candidates) != 1:
print "Couldn't determine a winner."
else:
print "The winner is:", final_candidates[0]
Конечно, теперь, когда тело функции очищено, вы сможете увидеть тривиально, как преобразовать в итерацию. Кроме того, здесь есть ненужное осложнение: поскольку мы определяем количество нарушений для каждого кандидата, нет смысла определять всех нарушителей и отфильтровывать их: мы могли бы просто определить всех хороших кандидатов напрямую через противоположное условие (слева). как упражнение).