Zanurkuj w Pythonie/Programowanie funkcyjne/Odwzorowywanie listy

Jeszcze o odwzorowywaniu list

edytuj

Wiecie już, w jaki sposób użyć wyrażeń listowych w celu odwzorowania jednej listy w inną. Można to osiągnąć również w inny sposób, używając wbudowanej funkcji map. Działa ona podobnie do funkcji filter.

Przykład 16.10. Wprowadzenie do funkcji map

>>> def double(n):
...     return n*2
...     
>>> li = [1, 2, 3, 5, 9, 10, 256, -3]
>>> map(double, li)                       #(1)
[2, 4, 6, 10, 18, 20, 512, -6]
>>> [double(n) for n in li]               #(2)
[2, 4, 6, 10, 18, 20, 512, -6]
>>> newlist = []
>>> for n in li:                          #(3)
...     newlist.append(double(n))
...     
>>> newlist
[2, 4, 6, 10, 18, 20, 512, -6]
  1. Funkcja map pobiera jako parametry funkcję oraz listę[1], a zwraca nową listę, która powstaje w wyniku wywołania funkcji przekazanej w pierwszym parametrze dla każdego elementu listy przekazanej w drugim parametrze. W tym przypadku każdy element listy został pomnożony przez 2.
  2. Ten sam efekt można osiągnąć wykorzystując wyrażenia listowe. Wyrażenia listowe pojawiły się w języku Python w wersji 2.0; funkcja map istniała w języku od zawsze.
  3. Jeśli bardzo chcecie myśleć jak programista Visual Basica, to moglibyście również do tego celu użyć pętli.

Przykład 16.11. Funkcja map z listami zawierającymi elementy różnych typów

>>> li = [5, 'a', (2, 'b')]
>>> map(double, li)                       #(1)
[10, 'aa', (2, 'b', 2, 'b')]
  1. Chciałbym zwrócić uwagę, że funkcja, której używamy jako argumentu map może być bez problemu zastosowana do list zawierających elementy różnych typów, o ile oczywiście poprawnie obsługuje każdy z typów, jakie posiadają elementy listy. W tym przypadku funkcja double mnoży swój argument przez 2, a Python wykona w tym momencie operację właściwą dla typu tego argumentu. W przypadku wartości całkowitych oznacza to pomnożenie wartości przez 2; w przypadku napisów oznacza to wydłużenie napisu o samego siebie; dla krotek oznacza to utworzenie nowej krotki zawierającej wszystkie elementy krotki oryginalnej, a po nich ponownie wszystkie elementy krotki oryginalnej.

Dobra, koniec zabawy. Popatrzmy na kawałek prawdziwego kodu.

Przykład 16.12. Funkcja map w regression.py

    filenameToModuleName = lambda f: os.path.splitext(f)[0] #(1)
    moduleNames = map(filenameToModuleName, files)          #(2)
  1. Jak widzieliśmy w podrozdziale 4.7 Wyrażenia lambda, lambda pozwala na zdefiniowanie funkcji w locie. W przykładzie 6.17 “Rozdzielanie ścieżek” (w podrozdziale Praca z katalogami), os.path.splitext pobiera nazwę pliku i zwraca parę (nazwa, rozszerzenie). A więc funkcja filenameToModuleName bierze nazwę pliku jako parametr i odcina od niej rozszerzenie, zwracając nazwę bez rozszerzenia.
  2. Wywołanie map spowoduje wywołanie funkcji filenameToModuleName dla każdego elementu z listy files , a w rezultacie zwrócenie listy wartości, jakie powstały po każdym z tych wywołań. Innymi słowy, odcinamy rozszerzenie dla każdej nazwy pliku i zbieramy powstałe w ten sposób nazwy bez rozszerzeń do listy moduleNames.

Jak zobaczycie w dalszej części rozdziału, tego typu myślenie, które jest mocno skoncentrowane na przetwarzanych danych, można rozciągnąć przez wszystkie etapy pisania kodu, aż do ostatecznego celu, jakim jest zdefiniowanie i uruchomienie pojedynczego zestawu testów jednostkowych zawierającego testy ze wszystkich poszczególnych przypadków testowych.

Przypisy

  1. Patrz przypis w poprzednim podrozdziale, Filtrowanie listy