Ta strona wykorzystuje ciasteczka ("cookies") w celu zapewnienia maksymalnej wygody w korzystaniu z naszego serwisu. Czy wyrażasz na to zgodę?

Czytaj więcej
< All Topics
Print

Funkcje

Składnia funkcji

 

Możliwość zaprogramowania funkcji zwiększa uniwersalność pisanych skryptów. Można, a nawet trzeba, stosować je wtedy, gdy stosujemy ten sam schemat obliczeń, ale dla różnych parametrów. Przykładowo dane wyrażane w procentach chcemy korelować z ciężarem obiektów. Gdy dane procentowe skupiają się wokół 0 lub 100, trzeba je transformować funkcją x/(100-x) i takie transformowane dane użyć do obliczeń korelacji. Warto wtedy posługiwać się jedną funkcją T(x).

Funkcje tworzy się po słowie ‘def’ po którym występuje nazwa funkcji, nawiasy okrągłe z wymienionymi argumentami. Następnie występuje zestaw instrukcji, które służą do wyliczenia wyniku funkcji. Na końcu, po słowie ‘return’ pisze się to, co funkcja powinna pokazać jako wynik. Kod transformacji T wyglądałby następująco:

def T(x):
    y=x/(100-x)
    return y

Ponieważ ma ona sens tylko dla liczb między 0 a 100 warto takie ograniczenie wprowadzić w programie funkcji.

def T(x):
    if (x<0 | x>=100) : y='wartość wykluczająca transformację'
    y=x/(100-x)
    return y

Argumentami funkcji, jak i wynikiem, mogą być liczby, znaki, teksty, listy, krotki i inne złożone wyrażenia.

Aby zobaczyć wynik działania funkcji, musimy wstawić do niej jakieś wartości.

#funkcja
def T(x):
    if (x<0 | x>=100) : y='wartość wykluczająca transformację'
    y=x/(100-x)
    return y
#program
print(T(-1))
print(T(1.5),T(95))
print(T(100))
>>>
wartość wykluczająca transformację
0.005025125628140704 19.0
wartość wykluczająca transformację

Słowo return powinno pojawić się w funkcji tylko raz:

#funkcja
def potegi(x):
    for i in range(5): return x**i
#program
print(potegi(3))
print(potegi(5.5))
#funkcja
def potegi(x):
    return x**0
    return x**1
    return x**2
    return x**3
    return x**4
#program
print(potegi(3))
print(potegi(5.5))
>>>
1
1.0
>>>
1
1.0

W obu przypadkach dla obi liczb 3 i 5.5 zwrócony został tylko wynik dla pierwszego użycia return. Prawidłowy program powinien utworzyć listę wyników i nazwę tej listy pokazać jako wynik funkcji.

#funkcja
def potegi(x):
    wyniki=[]
    for i in range(5): wyniki.append(x**i)
    return wyniki
#program
print(potegi(3))
print(potegi(5.5))
>>>
[1, 3, 9, 27, 81]
[1.0, 5.5, 30.25, 166.375, 915.0625]

Zmienne zastosowane w funkcjach są zmiennymi wyłącznie w obrębie funkcji. te same nazwy mogą mieć zmienne poza funkcją i mogą mieć zupełnie inne wartości, a nawet typy.

#funkcja
def dzielenie(a,b):
    if b!=0: return a/b
    else: return 'dziel.przez.0'
#program
a='Wykonujemy dzielenie 75 jabłek '
b='między 15 uczniów i każdy uczeń dostaje ich '
print(a+b,dzielenie(75,15))
>>>
Wykonujemy dzielenie 75 jabłek między 15 uczniów i każdy uczeń dostaje ich 5.0

 

Funkcje wieloargumentowe

 

Większość funkcji, które trzeba napisać w Pythonie w postaci funkcji jest wieloargumentowa. Być może dlatego, że takich funkcji jest stosunkowo mało w standardowych bibliotekach.

def nazwa(a1,a2,...):
    instrukcja 1.1
    instrukcja 1.2
    ...
return(wartość)

Przykładem przydatnej funkcji może być funkcja analizująca liczbę pierwiastków rzeczywistych równania kwadratowego ax2+bx+c wyliczająca je, gdy wielkość “delta” jest nieujemna. Funkcja taka może wyglądać następująco:

def pierwiastki(a,b,c):
    delta=b*b-4*a*c
    if delta<0:
        komunikat="Nie ma pierwiastków"
        return komunikat
    else:
        komunikat="Ma pierwiastki"
        x1=(-b-(delta)**0.5)/(2*a)
        x2=(-b+(delta)**0.5)/(2*a)
        return komunikat, x1, x2

Żeby zobaczyć efekty jej działania należy wprowadzić przykładowe parametry:

#funkcja
def pierwiastki(a,b,c):
    delta=b*b-4*a*c
    if delta<0:
        komunikat="Nie ma rzeczywistych pierwiastków."
        return komunikat
    else:
        komunikat="Ma pierwiastki rzeczywiste:"
        x1=(-b-(delta)**0.5)/(2*a)
        x2=(-b+(delta)**0.5)/(2*a)
        return komunikat, x1, x2
#Program
print(pierwiastki(4,7,3))
print(pierwiastki(4,7,4))
>>>
('Ma pierwiastki rzeczywiste:', -1.0, -0.75)
Nie ma rzeczywistych pierwiastków

Aby wśród wyników programu pojawiły się wprowadzone wartości parametrów, należy trochę inaczej napisać skrypt.

def pierwiastki(a,b,c):
    delta=b*b-4*a*c
    if delta<0:
        komunikat="Nie ma pierwiastków"
        return komunikat
    else:
        komunikat="Ma pierwiastki"
        x1=(-b-(delta)**0.5)/(2*a)
        x2=(-b+(delta)**0.5)/(2*a)
        return komunikat, x1, x2
a=4
b=7
c=3
print("wielomian "+str(a)+"x^2+"+str(b)+"x+"+str(c), str(pierwiastki(4,7,3)))
>>>
wielomian 4x^2+7x+3 ('Ma pierwiastki', -1.0, -0.75)

Komunikaty i wyniki zwracane przez funkcje mogą być jeszcze bardziej złożone.

def pierwiastki(a,b,c):
    tresc="wielomian "+str(a)+"x^2+"+str(b)+"x+"+str(c)+" "
    delta=b*b-4*a*c
    if delta<0:
        komunikat="nie ma pierwiastków"
        return tresc+komunikat
    else:
        komunikat="ma pierwiastki: "
        x1=(-b-(delta)**0.5)/(2*a)
        x2=(-b+(delta)**0.5)/(2*a)
        return tresc+komunikat+str(x1)+","+str(x2)
a=4
b=7
c=3
print(pierwiastki(a,b,c))
c=4
print(pierwiastki(a,b,c))
>>>
wielomian 4x^2+7x+3 ma pierwiastki: -1.0,-0.75
wielomian 4x^2+7x+4 nie ma pierwiastków 

Funkcje mogą posiadać nie tylko dowolną ilość argumentów (łącznie z 0), a także zwracać dowolną liczbę wartości. Nazywa się je funkcjami zwracającymi wektor wartości. Zapisuje się to w matematyce jako wektor funkcji, a Pythonie najlepiej zapisywać ich wynik jako krotkę.

def dzialania(a,b):
    if b!=0: dziel=a/b
    else: dziel='dziel.przez.0'
    return a+b, a-b, a*b, dziel
print(dzialania(3,4))
print(dzialania(5,0))
>>>
(7, -1, 12, 0.75)
(5, 5, 0, 'dziel.przez.0')

Zarówno argumentami jak i wartościami funkcji w Pythonie mogą być obiekty złożone, np. listy. Poniższa funkcja przekłada elementy list w sposób deterministyczny.

def przestaw(lista):
    lis=[]
    i=0
    while i<len(lista):
        lis=[lista[i]]+lis
        i=i+1
        if i<len(lista):
            lis=lis+[lista[i]]
            i=i+1
    return lis
print(przestaw([1,2,3,4,5]))
def przestaw(lista):
    lis=[]
    i=0
    while i<len(lista):
        lis.insert(0,lista[i])
        i=i+1
        if i<len(lista):
            lis.append(lista[i])
            i=i+1
    return lis
print(przestaw([1,2,3,4,5]))
>>>
[5, 3, 1, 2, 4]
>>>
[5, 3, 1, 2, 4]

Można też zdefiniować funkcję wykonująca działania na elementach list. Jest to odpowiednik funkcji posiadającej dowolną, nie określoną z góry, liczbę parametrów. Takimi funkcjami jest szereg charakterystyk rozkładu używanych przy opracowaniu danych. Można je zdefiniować używając znaku * przed nazwą listy.

def srednia(*lista):
    suma=0
    for x in lista: suma=suma+x
    if len(lista)>=1: return suma/len(lista)
    else: return 'brak'
print(srednia(1))
print(srednia(1,2,3))
print(srednia(1,1,3,3,1.5,4))
def wariancja(*lista):
    n=len(lista)
    suma=0
    sumakw=0
    for x in lista:
        suma=suma+x
        sumakw=sumakw+x**2
    if len(lista)<2: return "brak"
    else: return (n/(n-1))*(sumakw/n-(suma/n)**2)
print(wariancja(1))
print(wariancja(1,2,3))
print(wariancja(1,1,3,3,1.5,4))
>>>
1.0
2.0
2.25
>>>
brak
1.0000000000000004
1.575

 

Funkcje z parametrami

W matematyce bardzo często rozróżnia się argumenty funkcji od jej parametrów. W zapisie wielomianu kwadratowego ax2+bx+c argumentem jest x a litery a,b,c są parametrami za którymi mogą się kryć liczby rzeczywiste lub zespolone. Rzadko rozważa się funkcje czteroargumentowe f(x,a,b,c)=ax2+bx+c, aczkolwiek taka funkcja jest prawidłowo zdefiniowana zarówno dla matematyków jak i programów komputerowych. Różnica polega tylko na różnej interpretacji tych symboli, czego komputer sam z siebie nie robi.

W Pythonie wielomian kwadratowy jest funkcja czteroargumentową. Można go zdefiniować następująco:

 

def wiel_kwadrat(x,a,b,c):
    return a*x**2+b*x+c

Jeżeli wykluczymy z grupy wielomianów kwadratowych funkcje liniowe (powstające, gdy a=0), to powinniśmy to uwzględnić w funkcji:

def wiel_kwadrat(x,a,b,c):
    if a==0: return "To funkcja liniowa"
    else: return a*x**2+b*x+c

Ponadto niektórym parametrom można nadać wartości domyślne uznając przykładowo, że podstawowy wielomian kwadratowy to y=x2.

def wiel_kwadrat(x,a=1,b=0,c=0):
    if a==0: return "To funkcja liniowa"
    else: return a*x**2+b*x+c

Spowoduje to, że bez podania wartości parametrów zostaną im przypisane wartości domyślne. Można zatem używać takiej funkcji jako pozornie jednoargumentowej albo dwu- lub trzyargumentowej.

def wiel_kwadrat(x,a=1,b=0,c=0):
    if a==0: return "To funkcja liniowa"
    else: return a*x**2+b*x+c
print(wiel_kwadrat(-2))
print(wiel_kwadrat(-2,a=0.5))
print(wiel_kwadrat(-2,b=2))
print(wiel_kwadrat(-2,a=0.5,b=2),wiel_kwadrat(-2,b=2,a=0.5))
print(wiel_kwadrat(-2,a=0.5,b=2,c=-1),wiel_kwadrat(-2,c=-1,a=0.5,b=2))
>>>
4
2.0
0
-2.0 -2.0
-3.0 -3.0

Jeżeli stosujemy zdefiniowane w definicji funkcji nazwy parametrów i nadajemy im wartość to nie jest ważne w jakiej kolejności je napiszemy. Ale czesto pisze się tylko wartości liczbowe. Wtedy kolejność jest ważana, bo przypisanie wartości do parametrów przebiega według ich kolejności w definicji funkcji.

def wiel_kwadrat(x,a=1,b=0,c=0):
    if a==0: return "To funkcja liniowa"
    else: return a*x**2+b*x+c
print(wiel_kwadrat(-2,0.5,2,-3))
print(wiel_kwadrat(-2,2,0.5,-3))
print(wiel_kwadrat(-2,0.5,-3,2))
>>>
-5.0
4.0
10.0

 

Funkcje rekurencyjne

Definiowanie funkcji rekurencyjnych, takich które odwołują się do samych siebie, generalnie skraca kod. Nie zawsze wiąże się to z efektywnością, czyli krótkim czasem obliczeń. Dotyczy ono funkcji  definiowanych w postaci ciągu rekurencyjnego:

f(n)=F(f(n-1))

gdzie F(x) jest jakąś funkcja określoną na zbiorach wartości funkcji f(n). Definiowanie takiej funkcji można elegancko przedstawić w następującej postaci:

def f(n):
    if n==0: return f(0)
    else: return f(n)=F(f(n-1))

Przykładami takich funkcji są silnia: f(0)=1, f(n)=f(n-1)*n oraz kolejne wyrazy w n-tym rzędzie trójkąta Pascala: f(0,0)=1, f(1,0)=1, f(1,1)=1, f(n,0)=1, f(n,k)=f(n-1,k-1)+f(n-1,k) dla k≥0 i k≤n.

def silnia(n):
    if n==0: return 1
    else: return n*silnia(n-1)
print(silnia(1))
print(silnia(2))
print(silnia(10))
print(silnia(20))
def Pascal(n,k):
    if ((n==0 and k==0) or (n==1 and k==0) or (n==1 and k==1)):
        return 1
    else:
        if (n>1 and k==0): return 1
        elif (n>1 and k<=n): 
            return Pascal(n-1,k-1)+Pascal(n-1,k)
        else: return 0
print(Pascal(1,1))
print(Pascal(5,3))
print(Pascal(20,5))
print(Pascal(20,10))
>>>
1
2
3628800 2432902008176640000
>>>
1
10
15504
184756

Druga funkcja dla większych liczb wyraźnie zwalnia. Dzieje się to dlatego, że dla każdego n>1 uruchamia 2n-1 razy sama siebie i wielokrotnie wylicza ten sam wyraz znajdujący się na trójkącie Pascala. Przy takiej ilości obliczeń czas wyliczenia np. Pascal(100,20) musi być odczuwalny. To samo lepiej jest zrobić metodą iteracyjną:

def Pascal(n,k):
    pas=[[1],[1,1]]
    for j in range(2,n+1):
        rzad=[pas[j-1][0]]
        for i in range(1,j):
            rzad.append(pas[j-1][i-1]+pas[j-1][i])
        rzad.append(pas[j-1][j-1])
        pas.append(rzad)
    return pas[n][k]
print(Pascal(5,3))
print(Pascal(20,5))
print(Pascal(200,35))
>>>
10
15504
1407159943034720687164247227892896168560

Funkcja ta tylko pozornie wykonuje zbyt dużo obliczeń wyliczając wszystkie wartości w n-tym rzędzie trójkąta Pascala. Robi to jednak bardzo efektywnie i ostatecznie wyliczenie jej wartości dla dużych n nie trwa długo.

Spis treści