Каждый раз, когда вы вызываете метод как генератор (например, for x in get_reverse_iterator()
), python начинает выполнять этот метод построчно.Всякий раз, когда он достигает yield
, он останавливается и возвращает это.Когда на следующей итерации цикла for
его запрашивают значение next()
, он продолжает выполняться.
Это выглядит как довольно простая идиома обхода связанного списка, где каждый элемент списка содержит данные, которые сами являются списком (или некоторым другим итеративным значением, например строкой):
list[0].data = [1, 2, 3, 4]
list[1].data = [5, 6, 7, 8]
...
list[9].data = [37, 38, 39, 40]
Итак, что делает код здесь печатает эти подсписки от задней части основного списка до передней части основного списка.Вывод должен выглядеть примерно так:
37 38 39 40 33 34 35 36 ... 5 6 7 8 [1, 2, 3, 4]
, что становится очевидным, когда вы смотрите на то, как выполняется код.Я перепишу это словами:
func get_reverse_iterator(head) {
if head isn't the last element of the list, then
call this function on the next element of the list (head.next)
for every element in the return value of that,
yield that element
yield this element's data
«Базовый регистр» - это последний элемент списка, который не имеет .next
.Таким образом, его data
, который является итеративным, возвращается ко второму-последнему элементу.Второй по порядку элемент возвращает по очереди каждый элемент этих данных, а затем возвращает свои собственные данные третьему элементу.Элемент с третьего по последний возвращает по очереди каждый элемент этих данных и так далее, пока, наконец, вы не доберетесь до первого элемента списка.До сих пор каждый отдельный оператор yield
проходил один элемент вверх по цепочке, и поэтому внутренний цикл for
для первого элемента дал 36 значений.Наконец, все более поздние элементы в списке завершаются, пропуская значения, и поэтому первый элемент попадает в последний оператор функции и возвращает свои собственные данные.
Но ничего не осталось, чтобы поймать эти полученные данные ипроанализируйте его по отдельному элементу, чтобы он печатался как list
, как это было на первом месте.Или, по крайней мере, для моего примера, представленного выше.
В вашем случае это более просто, потому что, когда вы перебираете string
, каждый элемент по-прежнему равен string
.Но это то же самое в меньшем масштабе:
get_reverse_iterator()
вызывается в корневом узле lst
- Корневом узле (я назову его
NodeA
) имеет .next
get_reverse_iterator()
, вызываемый на следующем узле, который я буду называть NodeB
NodeB
имеет .next
get_reverse_iterator()
вызывается на следующем узле, который я назову NodeC
NodeC
не имеет .next
get_reverse_iterator(NodeC)
пропускает цикл for
ивыдает NodeC.data
, то есть 'c'` get_reverse_iterator(NodeB)
ловит 'c'
внутри цикла for
и возвращает его get_reverse_iterator(NodeA)
ловит 'c'
внутри for
loop и возвращает его 'c'
присваивается x
и печатается. - Происходит следующая итерация внешнего цикла, и выполнение возвращается к
get_reverse_iterator(NodeB)
- Цикл
for
заканчивается, потому что get_reverse_iterator(NodeC)
перестал давать вещи get_reverse_iterator(NodeB)
завершает цикл for
, выходит из блока if
и, наконец, возвращает NodeB.data
, то есть 'b'
get_reverse_iterator(NodeA)
захватывает 'b'
внутри цикла for
и возвращает его 'b'
присваивается x
и печатается. - Проходит следующая итерация внешнего цикла, и выполнение возвращается к
get_reverse_iterator(NodeA)
for
цикл заканчивается, потому что get_reverse_iterator(NodeC)
прекратил приносить вещи get_reverse_iterator(NodeA)
заканчивает цикл for
, выходит из блока if
и, наконец, выдает NodeA.data
, то есть 'a'
'a'
присваивается x
и печатается - Внешний цикл
for
завершается, так как get_reverse_iterator(NodeA)
перестает давать результаты.