15 октября 2012 г.

Циклы, углубляемся


Приемы программирования циклов

Цикл for - проще и быстрее чем цикл while, по крайней мере так оно выглядит. Но цикл for не всегда сможет сделать обход каких то элементов ( к примеру обход первого или же второго элемента в каком то типе данных). С такой задачей легко справляется цикл while. Но и для цикла for есть вспомогательные функции позволяющие управлять обходом элементов. 


  • Встроенная  функция range  возвращает  непрерывную  последовательность увеличивающихся  целых чисел, которые  можно использовать  в качестве индексов внутри цикла for


  • Встроенная  функция  zip  возвращает  список  кортежей,  составленных  из элементов входных списков с одинаковыми индексами, который может использоваться для одновременного обхода нескольких последовательностей в цикле for.
Счетные циклы: while и range :
Функция  range  является  по-настоящему  универсальным  инструментом,  который может использоваться в самых разных ситуациях. Чаще всего она используется для генерации индексов в цикле for, но вы можете использовать ее везде, где необходимы списки целых чисел. В Python 3.0 функция range возвращает итератор, который генерирует элементы по требованию, поэтому, чтобы отобразить результаты  ее  работы,  мы должны обернуть вызов  этой функции в вызов функции list.

>>> list(range(5)), list(range(2, 5)), list(range(0, 10, 2))
([0, 1, 2, 3, 4], [2, 3, 4], [0, 2, 4, 6, 8])
-----------------------------------------------------------------

Существует возможность воспроизводить последовательности чисел в диапазоне отрицательных значений и в порядке убывания:
>>> list(range(-5, 5))
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
>>> list(range(5, -5, -1))
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]
Такое использование функции range само по себе может быть полезным, однако чаще всего она используется в циклах for.
-----------------------------------------------------------------

Чтобы вывести три строки, можно использовать функцию range для создания соответствующего количества целых чисел – в версии 3.0 инструкция for автоматически извлекает все значения из итератора range, поэтому нам не потребовалось использовать функцию list:
>>> for i in range(3):
...     print(i, ‘Pythons’)
...
0 Pythons
1 Pythons
2 Pythons
-----------------------------------------------------------------

Обход последовательности:
>>> X = ‘spam’
>>> for item in X: print(item, end=’ ‘) # Простейший цикл
...
s p a m
-----------------------------------------------------------------

Если вам необходимо управлять логикой доступа к элементам, можно воспользоваться 
циклом while:
>>> i = 0
>>> while i < len(X):          # Обход с помощью цикла while 
...     print(X[i], end=’ ‘) 

...     i += 1
...
s p a m
-----------------------------------------------------------------

Однако  управлять  индексами  вручную можно  и  в цикле for, если  использовать функцию range для воспроизведения списка индексов.

>>> X
‘spam’
>>> len(X)                          # Длина строки
4
>>> list(range(len(X)))             # Все допустимые смещения в X
[0, 1, 2, 3]
>>>
>>> for i in range(len(X)): print(X[i],end=’ ‘)#Извлечение 
                                               #элементов вручную
...
s p a m
-----------------------------------------------------------------

Обход части последовательности: range и срезы
Последний пример вполне работоспособен, но он выполняется гораздо медленнее, чем мог бы. Кроме того, нам пришлось выполнить больше работы, чем требуется для решения такой задачи. Если вы не предъявляете особых требований к индексам, всегда лучше использовать простейшую форму цикла for – используйте цикл for вместо while везде, где только возможно, и используйте функцию range в циклах for, только если это действительно 
необходимо. Следующее простое решение является лучшим:
>>> for item in X: print(item) # Простейшая итерация
...

-----------------------------------------------------------------

Пропуск элементов:
>>> S = ‘abcdefghijk’
>>> list(range(0, len(S), 2))
[0, 2, 4, 6, 8, 10]
>>> for i in range(0, len(S), 2): print(S[i], end=’ ‘)
...
a c e g i k
Выбирается каждый второй элемент строки S.
-----------------------------------------------------------------

Чтобы получить каждый второй символ из строки S, можно извлечь срез с шагом 2:
>>> S = ‘abcdefghijk’
>>> for c in S[::2]: print(c, end=’ ‘)
...
a c e g i k
-----------------------------------------------------------------

Изменение списков: range
Необходимо прибавить 1 к каждому элементу списка.
Можно попытаться использовать для этой цели простейшую форму цикла for, но скорее всего это не то, что нам нужно:
>>> L = [1, 2, 3, 4, 5]
>>> for x in L:
...     x += 1
...
>>> L
[1, 2, 3, 4, 5]
>>> x
6
Такое решение вообще ничего не дает – здесь изменяется переменная цикла x,а не список L.
-----------------------------------------------------------------

Правильный вариант:
>>> L = [1, 2, 3, 4, 5]
>>> for i in range(len(L)):    # Прибавить 1 к каждому элементу в списке L
...     L[i] += 1              # Или L[i] = L[i] + 1
...
>>> L
[2, 3, 4, 5, 6]
-----------------------------------------------------------------

А возможно ли создать эквивалентный  цикл  while? Для  этого  нам  потребуется  приложить немного больше усилий, и такой цикл наверняка будет работать медленнее:
>>> i = 0
>>> while i < len(L):
...     L[i] += 1
...     i += 1
...
>>> L
[3, 4, 5, 6, 7]

-----------------------------------------------------------------

Параллельный обход: zip и map

Как было показано выше, встроенная функция range позволяет выполнять обход отдельных частей последовательностей. В том же духе встроенная функция 
zip позволяет использовать цикл for для обхода нескольких последовательностей параллельно. Функция zip принимает одну или несколько последовательностей в качестве аргументов и возвращает список кортежей, составленных из соответствующих элементов этих последовательностей. Например, предположим, что мы выполняем обработку двух списков:
>>> L1 = [1,2,3,4]
>>> L2 = [5,6,7,8]

Для объединения элементов  этих  списков можно использовать  функцию zip,которая  создаст список кортежей  из пар элементов (подобно  функции range,в версии 3.0 функция zip возвращает итерируемый объект, поэтому, чтобы вывести все результаты, возвращаемые этой функцией, необходимо обернуть ее вызов вызовом функции list):
>>> zip(L1, L2)
<zip object at 0x026523C8>
>>> list(zip(L1,L2))# Функция list необходима в 3.0, но не в 2.6
[(1, 5), (2, 6), (3, 7), (4, 8)]

Такой результат может пригодиться в самых разных ситуациях, но применительно к циклу for он обеспечивает возможность выполнения параллельных итераций:
>>> for (x, y) in zip(L1, L2):
...     print(x, y, ‘--’, x+y)
...
1 5 -- 6
2 6 -- 8
3 7 -- 10
4 8 -- 12
Здесь  выполняется  обход результата обращения  к функции zip, то есть пар, составленных из элементов двух списков.
-----------------------------------------------------------------
Эквивалентная функция map в Python 2.6:
В Python 2.X имеется  родственная встроенная  функция  map, объединяющая элементы последовательностей похожим образом, но она не усекает результат по длине кратчайшей последовательности, а дополняет недостающие элементы значениями None, если аргументы имеют разную длину:
>>> S1 = ‘abc’
>>> S2 = ‘xyz123’
>>> map(None, S1, S2)                        # Только в 2.X
[(‘a’, ‘x’), (‘b’, ‘y’), (‘c’, ‘z’), (None, ‘1’), (None, ‘2’), (None,’3’)]
-----------------------------------------------------------------
Конструирование словаря с помощью функции zip:
>>> D1 = {‘spam’:1, ‘eggs’:3, ‘toast’:5}
>>> D1
{‘toast’: 5, ‘eggs’: 3, ‘spam’: 1}
>>> D1 = {}
>>> D1[‘spam’]  = 1
>>> D1[‘eggs’]  = 3
>>> D1[‘toast’] = 5
-----------------------------------------------------------------
Пример, если программа получает ключи и значения для словаря в виде списков во время выполнения, уже после того, как сценарий был написан:
>>> keys = [‘spam’, ‘eggs’, ‘toast’]
>>> vals = [1, 3, 5]
превращаем в словарь
>>> list(zip(keys, vals))
[(‘spam’, 1), (‘eggs’, 3), (‘toast’, 5)]
>>> D2 = {}
>>> for (k, v) in zip(keys, vals): D2[k] = v
...
>>> D2
{‘toast’: 5, ‘eggs’: 3, ‘spam’: 1}
-----------------------------------------------------------------

Генерирование индексов и элементов: enumerate
Функция  enumerate  возвращает объект-генератор  –  разновидность  объекта, 
который поддерживает протокол итераций. В двух словах: он имеет метод __next__, вызываемый встроенной функцией next и возвращающий кортеж (index, value) для каждого элемента списка.
>>> E = enumerate(S)
>>> E
<enumerate object at 0x02765AA8>
>>> next(E)
(0, ‘s’)
>>> next(E)
(1, ‘p’)
>>> next(E)
(2, ‘a’)


Получение индексов и элементов:
>>> S = ‘spam’
>>> offset = 0
>>> for item in S:
...     print(item, ‘appears at offset’, offset)
...     offset += 1
...
s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3
-----------------------------------------------------------------

Получение индексов и элементов с использованием enumerate :
>>> S = ‘spam’
>>> for (offset, item) in enumerate(S):
...     print(item, ‘appears at offset’, offset)
...
s appears at offset 0
p appears at offset 1
a appears at offset 2
m appears at offset 3

Вот и все... далее будем рассматривать итераторы...

1 комментарий: