TI/Funkcje, struktury danych, moduły

Z Brain-wiki
Wersja z dnia 08:53, 23 maj 2015 autorstwa Jarekz (dyskusja | edycje) (Utworzono nową stronę " ==Funkcje== ===Definicja=== Dlaczego funkcja nazywa się właśnie tak, a nie np. procedura? Tak jak w matematyce, w programowaniu funkcja to "przyporządkowanie", kt...")
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)

Funkcje

Definicja

Dlaczego funkcja nazywa się właśnie tak, a nie np. procedura? Tak jak w matematyce, w programowaniu funkcja to "przyporządkowanie", które otrzymawszy jakieś argumenty coś zwraca. Dlatego też w niektórych językach programowania jest rozróżnienie na funkcje i procedury — funkcje to takie twory które "wywołane" zwracają jakąś wartość, natomiast procedury po prostu coś robią i nie dostajemy żadnego wyniku. W pythonie nie ma procedur. Zatem czy:

def hello1():
    print "hello world"

Jest funkcją? A może należałoby napisać tak:

def hello2():
    return "hello world"

Oba twory są funkcjami, w pythonie nawet jeśli nie napiszemy explicite return, nasza funkcja coś zwraca — instrukcja return bez podanej wartości jest równoważna instrukcji return None.

Szablon:Wyjaśnienie


Zadanie

Porównaj powyższe funkcje:

x1 = hello1()
x2 = hello2() 
print x1, x2

Dostęp do zmiennych

Obiekty w Pythonie dzielą się na niezmienne (liczby, napisy, krotki) i takie, które można zmieniać (listy, słowniki,... i inne struktury danych).

  • Zastanów się, jakie będą wyniki wywołania następujacych instrukcji. Pamiętaj o tym, że argumentem przekazywanym funkcji jest obiekt. W zadaniach może okazać się, że kod jest błędny i musisz go poprawić.
  1. Zmienne lokalne i globalne
    x = 6
    def fun(x):
        x += 1
        return x
    
    print fun(x)
    print x
    
    x = 6
    def fun(x):
        global x
        x += 1
        return x
    print fun(x)
    print x
    
    x = 6
    def fun(x):
        x += 1
        return x
    x = 7
    print fun(x)
    print x
    
    x = 6
    def fun(x):
        global x
        x += 1
        return x
    x = 7
    print fun(x)
    print x
    
    x = 6
    def fun(x):
        def fun1():
            global x
            x += 1
            return x
        x = 0
        return x
    x = 7
    print fun(x)
    print x
    
    # zmienne lokalne i globalne
    def modify0(anum):
        global x
        x = anum
        y = anum
        return (x,y)
    
    x = y = 5
    print modify0(3)
    print x, y
    

    Wniosek: global x powoduje, ze x jest tą samą zmienną co zmienna x zdefiniowana poza funkcją — zmiana x wewnątrz funkcji jest widoczna też na zewnątrz. W pozostałych przypadkach, nawet jeśli zmienne mają takie same nazwy jak jakies zmienne zdefioniowane na zewnątrz funkcji, zmiany nie są widoczne na zmiennych na zewnątrz. Jak to działa? Każda funkcja ma własny "konktekst wykonania", i jesli zmienna nie jest poprzedzona słówkiem global, to do kontekstu wykonania funkcji nie jest przekazywana zmienna z kontekstu wykonania programu, tylko jest tworzona nowa zmienna.

  2. def fun0():
        y = 5
        global x
        def fun1():
            global y
            y = 0
            return y
        fun1()
        print y    
        x += y
        return x
        
    y = 7
    x = 10
    
    fun0()
    print x
    print y
    

    Słówko global wiąże zmienną ze zmienną zdefiniowaną bezpośrednio w module, a nie ze zmienną z "nadfunkcji".

  3. # liczby jako argument
    
    def modify5(anum):
        anum += 1
    
    def modify6(anum):
        anum = 0
    
    x = 10
    
    modify5(x)
    print x
    modify6(x)
    print x
    

    Przekazywaliśmy wszystko przez wartość, więc zmienne są lokalne, zmiany nie są widoczne na zewnątrz.

  4. # napisy jako argument
    
    def modify7(astring):
        astring.append("a")
    
    s = "test"
    
    modify7(s)
    print s
    

    Na napisie w ogóle nie można takiej operacji wykonać.

  5. # listy jako argumenty
    
    def modify1(alist):
        alist.append(5)
    
    def modify2(alist):
        alist[0] += 3
    
    def modify3(alist):
        alist = []
    
    def modify4(alist):
        alist = [2*x for x in alist]
    
    lista = [1,2,3,4]
    
    modify1(lista)
    print lista
    modify2(lista)
    print lista
    modify3(lista)
    print lista
    modify4(lista)
    print lista
    
    def const_lst(item,lst=[]):
        lst.append(item)
        return lst
    
    L1 = const_lst(1)
    L2 = const_lst(11)
    
    print L1,L2
    

    Tutaj ponownie przekazywaliśmy wszystko przez wartość. Więc jak to jest, że czasem wynik jest widoczny na zewnątrz, a czasem nie? Czym jest tak na prawdę wartość, którą przekazujemy w przypadku list?

  • Wytłumacz otrzymane wyniki, korzystając z pojęcia obiektu niezmienniczego i takiego, który można zmieniać.


Szablon:Wyjaśnienie

Przekazywanie argumentów

Odgadnij jaki będzie wynik następnujących instrukcji:

  1. def func(arg1, arg2=5):
        def inner_func(arg3 = arg2):
            return arg1, arg3
        arg1 = arg2 = 15
        return inner_func
    
    f = func(1,2)
    print f()
    f2 = func(0)
    print f2()
    
  2. def nostar(a): print a
    
    def onestar(*a): print a
    
    def twostar(**a): print a
    
    nostar(1)
    nostar(1,2,3)
    
    onestar(1,2,3)
    
    twostar(a=1,b=2,c=3)
    
    twostar(1,2,3)
    
    def somearguments(a,b,c,d): print a,b,c,d
    
    somearguments(1)
    somearguments(1,2,3,4)
    x = (1,2,3,4)
    somearguments(x)
    somearguments(*x)
    somearguments(**x)
    x = {'a':1, 'b':2, 'c':3, 'd':4}
    somearguments(x)
    somearguments(*x)
    somearguments(**x)
    

Wyrażenie lambda

Wyrażenie lambda pojawiło się już w zeszłym roku.

Wyrażenie lambda umożliwia zapisanie jednolinijkowej funkcji bezpośrednio w kodzie programu bez potrzeby definiowania jej.

Standardowo definiując funkcję zwiększającą swój argument o 1, napisalibyśmy:

def zwieksz(x):
   return x+1

i wywołalibyśmy ją w programie:

print zwieksz(4)

Używając wyrażenia lambda, można zapisać to inaczej:

zwieksz = lambda x: x + 1
print zwieksz(4)

Możliwa jest też taka forma użycia:

g = (lambda x: x + 1)(4)
print g

Program też wypisuje wtedy na ekran 5.

Wygodnym zastosowaniem konstrukcji lambda jest funkcja map — map(funkcja, lista) zwraca listę z wynikami wykonania funkcji funkcja na elementach listy lista. Można zatem napisać tak:

res = map(lambda x: 3*x, [1,2,3,4])

dostajemy res = [3, 6, 9, 12] Albo tak:

res = map(lambda x,y: x+y, [1,2,3,4], [5,6,7,8] )

Wyrazenie dodaje nam dwie listy element po elemencie: res = [6, 8, 10, 12].

Moduły

Zadanie domowe

Zapisz wszystkie programy rysujące figury na ekranie do oddzielnych funkcji i zrób z nich moduł. Dodaj do niego funkcję rysującą trójkąt o boku [math]N[/math] i wysokości [math]\frac{N-1}{2}[/math] znaków. Załóż, że pary znaków [] są kwadratowe. Skorzystaj ze wskazówki podanej na poprzednich zajęciach.

........[]........

......[][][]......

....[][][][][]....

..[][][][][][][]..

[][][][][][][][][]

1 2 3 4 5 6 7 8 N

Zadanie domowe

Zbierz wszystkie swoje funkcje pisane na zajęcia z Analizy Sygnałów i złóż w jeden moduł. Opatrz je docstringami. Autor najlepszego modułu (najbardziej kompletnego, z funkcjami z najlepszymi modułami) dostanie dodatkową piatkę (liczącą się do oceny końcowej), a jego moduł zostanie oficjalną ściągawką używaną na zajęciach z Analizy Sygnałów.

Struktury danych

Sekwencje

Lista

Napisz polecenie, które:

  • doda element x na koniec listy l,
  • połączy listy l1 i l2,
  • na pozycje i listy l wstawi element x (tzn pomiędzy element i a i+w włoży nowy element),
  • usunie z listy l pierwsze wystąpienie elementu x,
  • usunie z listy l piąty element,
  • poda indeks pierwszego wystąpienia elementu x na liscie l,
  • zwróci liczbę wystąpień elementu x na liście l,
  • posortuje listę l,
  • utworzy odwrotność listy l1,
  • mając daną listę l1 liczb naturalnych utworzy listę l2, taką że l2[i] = l1[i] + i,
  • mając daną listę l1 liczb naturalnych utworzy listę l2, taką że l2[i] = l1[i] + 1 jeśli l1[i] jest parzyste, oraz l2[i] = l1[i] w przeciwnym przypadku.
Wstęp do struktur danych: stos
Idea stosu.

Mówiąc abstrakcyjnie, czysto teoretycznie, stos jest to taki "obiekt", "zbiór", "struktura danych", która posiada:

  • bufor z danymi,
  • operacje:
    • push() — dodaje element na "wierzchołek" stosu,
    • pop() — zdejmuje element, który został ostatnio dodany z wierzchołka stosu.

Zupełnie tak, jak na rysunku Figure 1.

Taka jest niezależna od konkretnego języka programowania definicja stosu. W różnych językach programowania taka struktura może być różnie zaimplementowana.

W pythonie listy bardzo dobrze służą jako stos, udostępniając następujące operacje:

  • append() implementuje operację push() (tzn. ma takie działanie jak nasz teoretyczny push() w stosie),
  • pop() działa dokładnie jak nasz "teoretyczny" pop().

Ćwiczenia

  • wypisze elementy od 3 do 20 listy l,
  • utwórz listę 100 zer.
Zadanie (właściwości obiektów, które nie są niezmienne)

Jaki będzie wynik następujących instrukcji i dlaczego:

a = ['1', '2', '3', '4', '5']
b = c = a
a.pop()
print a, b, c
a = ['1', '2', '3', '4', '5']
b = c = a
a.pop()
a = ['aaa', 'bbb', 'ccc']
print a, b, c

Krotka

Kolejka

Schemat kolejki.

Kolejka jest to taka struktura danych, która posiada bufor na dane oraz udostępnia operacje:

  • pop(): wyjmuje element, który jako pierwszy pojawił się w kolejce (analogicznie do kolejki w sklepie — ten kto przyszedł pierwszy jest jako pierwszy obsługiwany),
  • push(): dokłada element na koniec kolejki.

Pythonowy obiekt deque znajdujący się w module collections dobrze implementuje kolejkę (będący w istocie kolejka dwukierunkową):

  • operacja append() spełnia funkcję abstrakcyjnego push(),
  • operacja popleft() spełnia funkcję abstrakcyjnego pop().

Stos vs Kolejka: zastosowania

  • Omów czym się różnią operacje pop() i push() w kolejce oraz stosie.

Słowniki

Problemy

Wskazówka

W poniższych zadaniach warto jest skorzystać z funkcji loadtxt z modułu numpy.

Splot i korelacja

Szablon:TI/Splot i korelacja

Odrzucanie out-lierów

Szablon:TI/Outliery

Powierzchnia wielokąta

Szablon:TI/Wielokat