|
Instant Hacking Nauka programowania na przykładzie Pythona Magnus Lie Hetland tłum. Wojciech Warczakowski Jest to krótkie wprowadzenie do sztuki programowania, z wykorzystaniem przykładów napisanych w języku Python. (Jeśli już wiesz jak programować, a potrzebujesz krótkiego wprowadzenia do języka Python, możesz skorzystać z mojego artykułu Instant Python.) Artykuł ten został przetłumaczony na język włoski i obecnie trwa tłumaczenie wersji koreańskiej. Strona ta nie traktuje o włamywaniu się do systemów komputerowych innych ludzi. Jeśli jesteś zainteresowany tym tematem zobacz stronę Happy Hacker. Uwaga: Aby zamieszczone przykłady działały poprawnie pisz je do pliku tekstowego a następnie uruchamiaj interpreterem; nie próbuj uruchamiać programów w trybie interaktywnym - nie wszystkie będą działać. (Nie pytaj mnie dlaczego. Sprawdź dokumentację). (Pochwała za artykuł. Main Python page) 1. Środowisko 2. Czym jest programowanie?
Sernik na kruchym cieście
Składniki:
Ciasto:
30 dag (1 1/2 szklanki) mąki
13 dag margaryny
5 dag (3 łyżki) cukru
4 żółtka
Masa serowa:
50 dag sera twarogowego tłustego
20 dag (niepełna szklanka) cukru
13 dag margaryny
4-5 jaj
2 dag (1 łyżka) kaszy manny
cukier waniliowy lub esencja zapachowa
Sposób przyrządzenia:
Margarynę posiekać nożem z mąką, a następnie połączyć z żółtkami i
cukrem. Ciasto szybko wyrobić ręką i schłodzić w lodówce (co najmniej
30 min). Schłodzone ciasto rozwałkować na grubość 1/2 cm i przenieść
wałkiem na blachę. Ciasto lekko podpiec w gorącym piekarniku (15-20
min). Przyrządzić masę serową: ser przepuścić przez maszynkę.
Margarynę utrzeć z cukrem, żółtkami, kaszą manną i stopniowo dodawanym
serem. Ubić sztywną pianę z białek i delikatnie połączyć z masą
serową. Masę wyłożyć na podpieczony spód i piec 30-40 min w
umiarkowanie gorącym piekarniku.
Oczywiście, żaden komputer tego nie zrozumie... A większość komputerów nie byłaby zdolna do zrobienia sernika nawet, jeśli zrozumiałyby ten przepis. Zatem, co musimy zrobić, aby przepis stał się bardziej przyjazny dla komputera? Zasadniczo dwie rzeczy. Musimy (1) mówić w taki sposób, aby komputer nas zrozumiał, i (2) mówić o rzeczach, które jest on w stanie wykonać. Pierwszy punkt oznacza, że musimy użyć języka - języka programowania zrozumiałego dla naszego interpretera (tłum. programu tłumaczącego), a drugi punkt oznacza, że nie możemy oczekiwać, aby komputer zrobił sernik - ale możemy oczekiwać, aby dodawał liczby i wypisywał różne teksty na ekranie. 3. Witaj...
print "Witaj, świecie!"
Zasadniczo program ten jest podobny do przedstawionego wcześniej przepisu na sernik (oczywiście jest on znacznie krótszy!). Program mówi komputerowi, co ma zrobić: wypisać na ekranie "Witaj, świecie!". Łatwizna. A jeśli chcielibyśmy, aby komputer wykonał coś trudniejszego.
print "Witaj, świecie!"
print "Żegnaj, świecie!"
Niezbyt trudne, nieprawdaż? I niezbyt interesujące... Chcemy zrobić
coś ze składnikami, tak jak w przepisie na sernik. No
dobrze - jakie mamy składniki? Mamy ciągi znaków, jak
# Pole prostokąta
# Składniki:
szerokosc = 20
wysokosc = 30
# Sposób przyrządzenia:
pole = szerokosc*wysokosc
print pole
Widzisz zapewne podobieństwo (choć niewielkie) do przepisu na sernik.
Ale w jaki sposób działa ten program? Po pierwsze, linie rozpoczynające się
Linie takie jak
szerokosc*wysokosc
jest w zasadzie tym samym co
20*30
które z kolei równe jest 600. Wartość ta następnie zostaje przypisana zmiennej "pole". Ostatnie wyrażenie programu wypisuje na ekran wartość zmiennej "pole"
600
Uwaga: W niektórych językach już na początku programu musisz powiedzieć komputerowi jakich zmiennych będziesz używał (jakich składników użyjesz do przygotowania sernika) - w Pythonie zmienne możesz wymyślać w trakcie pisania programu. 4. Komunikacja z komputerem
promien = 30
print promien*promien*3.14
Jednak program ten, podobnie jak poprzedni, jest mało interesujący - przynajmniej moim zdaniem. Jest on mało elastyczny. No bo, co będzie jeśli interesujące nas koło ma promień 31 stopni? Czy komputer może o tym wiedzieć? Sytuacja przypomina trochę przepis na sernik: "Masę serową wyłożyć na podpieczony spód i piec 30-40 min. w umiarkowanie gorącym piekarniku." Skąd możemy wiedzieć czy po 30 minutach sernik jest już upieczony? Musimy to sprawdzić. W jaki sposób komputer dowie się o promieniu naszego koła? Musi nas o to zapytać... My zaś musimy w programie powiedzieć mu, aby zapytał się o promień:
promien = input("Jaki jest promień?")
print promien*promien*3.14
input
nie zrobi wiele... Musisz wstawić parę nawiasów na końcu słowa input.
Zatem Ale w jaki sposób odpowiedź trafia do zmiennej
foo = input
bar = input()
5. Sterowanie To czego potrzebujemy nazywa się sterowaniem programem. Program może wykonać dwa działania - albo wyciągnąć sernik z piekarnika, albo pozostawić go. Możemy wybierać, a warunkiem jest, czy sernik jest już upieczony, czy nie. Technika ta nazywana jest warunkowym wykonaniem programu. Program może wyglądać tak:
temperatura = input("Jaka jest temperatura sernika?")
if temperatura > 30:
print "Sernik jest już upieczony."
else:
print "Musisz troszkę poczekać na sernik."
Znaczenie programu powinno być oczywiste: Jeśli temperatura jest wyższa niż 30st.C, wówczas wypisz komunikat mówiący użytkownikowi, że sernik jest już upieczony, w przeciwnym razie powiedz użytkownikowi, że musi jeszcze troszkę poczekać. Uwaga: Wcięcia w języku Python są ważne.
Bloki w wykonaniu warunkowym (i pętle oraz
definicje funkcji - patrz niżej) muszą mieć wcięcia (wcięcia z tą
samą liczbą białych znaków; jeden Wróćmy do naszych obliczeń pola. Czy widzisz, co ten program robi?
# Program obliczający pole
print "Witamy w programie obliczającym pole"
print "------------------------------------"
print
# Wydrukuj menu:
print "Wybierz figurę:"
print "1 Prostokąt"
print "2 Koło"
# Czekaj na wybór użytkownika:
figura = input("> ")
# Oblicz pole:
if figura == 1:
wysokosc = input("Wpisz wysokość: ")
szerokosc = input("Wpisz szerokość: ")
pole = wysokosc*szerokosc
print "Pole wynosi", pole
else:
promien = input("Wpisz promien: ")
pole = 3.14*(promien**2)
print "Pole wynosi", pole
Nowe rzeczy w tym przykładzie...
Program ten jest dosyć prosty: Pyta o liczbę, która mówi mu, czy
użytkownik chce obliczyć pole prostokąta, czy koła. Następnie, korzysta z
Ćwiczenie:
if foo == 1:
# Zrób coś...
elif foo == 2:
# Zrób coś innego...
elif foo == 3:
# Zrób coś zupełnie różnego...
else:
# Jeśli żaden z warunków nie został spełniony...
6. Pętle Python ma dwa rodzaje pętli: while-pętle i for-pętle. For-pętle są chyba najprostsze. Na przykład:
for jedzenie in "ciasto", "jajka", "pomidory":
print "Kocham", jedzenie
Co oznacza: dla każdego elementu w liście
for liczba in range(1,100):
print "Witaj, świecie!"
print "Tylko", 100 - liczba, "pozostało..."
print "Witaj, świecie"
print "To był ostatni... Fju!"
Funkcja Zawartość pętli jest wykonywana dla (for) każdej liczby w (in) przedziale (range) od (włącznie) 1 do (bez) 100. (Wyjaśnienie tego, co robi ciało pętli i następne wyrażenia pozostawiam jako ćwiczenie dla ciebie.) Jednak to nam nie rozwiązuje naszego problemu z pieczeniem. Jeśli
chcemy sprawdzić sernik sto razy, wówczas to rozwiązanie byłoby na
miejscu, ale my nie wiemy czy sto razy wystarczy, czy może będzie to za dużo.
Po prostu chcemy sprawdzać sernik, kiedy nie jest on wystarczająco
gorący (lub do czasu, kiedy stanie się wystarczająco
gorący). Dlatego użyjemy
# Program pieczenia sernika
# Pobierz funkcję sleep
from time import sleep
print "Rozpocznij pieczenie sernika. (Wrócę za 3 minuty.)"
# Czekaj 3 minuty (3*60 sekund)...
sleep(180)
print "Wróciłem :)"
# Jak gorący jest sernik?
wystarczajaco_goracy = 30
temperatura = input("Jak gorący jest sernik? ")
while temperatura < wystarczajaco_goracy:
print "Niewystarczająco gorący... Piecz jeszcze chwilę..."
sleep(30)
temperatura = input("OK. Jak gorący jest teraz? ")
print "Wystarczająco gorący - gotowe!"
Nowe rzeczy w tym przykładzie...
Ćwiczenie 1 7. Większe programy - abstrakcja Czy już mieliśmy z czymś takim do czynienia? Tak. Zamiast dokładnie
mówić komputerowi w jaki sposób powinien odebrać odpowiedź od użytkownika (OK -
rzeczywiście nie umielibyśmy tego zrobić... Ale nie umielibyśmy
również zrobić twarogu, zatem... Powiedzmy, że chcemy znaleźć największą liczbę całkowitą, która jest
mniejsza od podanej liczby dodatniej. Na przykład, dla liczby 2.7 byłoby
to 2. Takie coś nazywane jest "floor" danej liczby. (Właściwie można by
skorzystać z wbudowanej w Python funkcji
liczba = input("Jaka jest liczba? ")
floor = 0
while floor < liczba:
floor = floor+1
floor = floor-1
print "Floor liczby", liczba, "wynosi", floor
Zauważ, że pętla kończy się, kiedy
def floor(liczba):
wynik = 0
while wynik < liczba:
wynik = wynik+1
wynik = wynik-1
return wynik
Nowe rzeczy w tym przykładzie...
Jeśli mamy już zdefiniowaną funkcję, możemy ją wykorzystać w ten oto sposób:
x = 2.7
y = floor(2.7)
Po wykonaniu naszej funkcji
def suma(x,y):
return x+y
Ćwiczenie 2
Wskazówki:
8. Więcej o funkcjach Ten rodzaj abstrakcji, którą używaliśmy podczas tworzenia fukcji często nazywany jest abstrakcją proceduralną, w wielu językach obok słowa function używa się również słowa procedure. Właściwie oba pojęcia są różne, jednak w Pythonie oba nazywane są funkcjami (ponieważ są one definiowane i używane mniej więcej w ten sam sposób.) Jaka jest różnica (w innych językach) między funkcjami a procedurami? Jak zauważyłeś w pierwszej części rozważań o funkcjach, funkcje mogą zwracać wartość. Różnica leży w procedurach, które nie zwracają takiej wartości. W wielu przypadkach, taki podział funkcji na dwa typy - te, które zwracają i te, które nie zwracają wartości - może byc użyteczny. Funkcja, która nie zwraca wartości ("procedura") wykorzystywana jest jako "podprogram". Wywołujemy taką funkcję, a program wykonuje pewne zadanie, takie jak ubijanie piany z białek lub cokolwiek. Możemy użyć tę funkcję w wielu miejscach bez przepisywania kodu. (Technikę tę nazywamy ponownym wykorzystaniem kodu (code reuse) - więcej o tym później.) Użyteczność takiej funkcji (lub procedury) leży w jej efektach ubocznych - funkcja zmienia swoje środowisko (poprzez na przykład mieszanie cukru z białkiem i ubijanie go). Spójrzmy na przykład:
def czesc(kto):
print "Cześć,", kto
czesc("świat")
# Wypisze "Cześć, świat"
Wypisanie tekstu jest uważane za efekt uboczny i ponieważ jest to wszystko, co ta funkcja wykonuje jest ona uważana za tzw. procedurę. Ale... W rzeczywistości nie zmieniła ona swojego środowiska, nieprawdaż? Jak może to zrobić? Spróbujmy:
# *Błędnie*
wiek = 0
def ustawWiek(a):
wiek = a
ustawWiek(100)
print wiek
# Drukuje "0"
Co tu jest źle? Problemem jest funkcja Uwaga: Zmienne globalne rzadko są wykorzystywane w Pythonie. Prowadzą one do złej struktury kody, lub do tzw. kodu spaghetti. Ja użyję je jako wprowadzenie do bardziej skomplikowanych technik - ty ,jeśli możesz, unikaj takich zmiennych. Poprzez poinformowanie interpretera, że zmienna jest globalna (poprzez
wyrażenie
# *Prawidłowo*, ale niezbyt ładnie
wiek = 0
def ustawWiek(a):
global wiek
wiek = a
ustawWiek(100)
print wiek
# Drukuje "100"
Gdy poznasz objekty (poniżej) zobaczysz, że lepszym sposobem wykonania
naszego zadania byłoby użycie objektu z właściwością A co z prawdziwymi funkcjami? Czym jest w rzeczywistości funkcja? Matematyczne funkcje są jak "maszyny", które otrzymują jakieś dane i obliczają z nich wynik. Za każdym razem zwracany będzie ten sam wynik jeśli funkcja otrzyma takie same dane. Przykład:
def kwadrat(x):
return x*x
Powyższe jest tym samym co funkcja matematyczna f(x)=x2. Zachowuje się ja funkcja dzięki temu, że opiera się tylko na danych wejściowych i nie zmienia w żaden sposób swojego środowiska. Zarysowałem dwa sposoby tworzenia funkcji: pierwszy typ jest podobny do
procedury i nie zwraca wyniku; drugi jest podobny do funkcji matematycznej
i nie robi nic (prawie) oprócz zwracania wyniku. Kiedy funkcja
zwraca coś powinno być jasne, że to robi. Powinieneś zatem signalizować
to poprzez jej nazwę, np. używając tylko rzeczownika "czystej" funkcji tak
jak 9. Więcej składników - struktury danych Jakie składniki do tej pory używaliśmy w naszych programach? Liczby i łańcuchy znaków. Prawda? Troszke nudne... Wprowadźmy kilka nowych składników, aby rzeczy stały się bardziej ciekawsze. Struktury danych są składnikami, które zawierają dane. (Niespodzianka, niespodzianka....) Pojedyncza liczba w rzeczywistości nie posiada żadnej struktury, zgadza się? Załóżmy jednak, że chcemy kilka liczb połączyć w jeden składnik, który mógłby mieć jąkąś strukturę. Na przykład, chcielibyśmy mieć listę liczb. Proszę bardzo:
[3,6,78,93]
O listach wspomniałem w części o pętlach, ale niewiele o nich powiedziałem. Zatem, w taki sposób możesz je tworzyć. Po prostu wypisz elementy listy, oddziel je przecinkami i zamknij w nawiasach kwadratowych. Napiszmy teraz przykład, który oblicza liczby pierwsze (liczby, które dzielą się tylko przez siebie lub 1):
# Oblicz wszystkie liczby pierwsze poniżej 1000
# (Nie jest to najlepszy sposób wykonania tego,
ale...)
wynik = []
kandydatki = range(3,1000)
baza = 2
produkt = baza
while kandydatki:
while produkt < 1000:
if produkt in kandydatki:
kandydatki.remove(produkt)
produkt = produkt+baza
wynik.append(baza)
baza = kandydatki[0]
produkt = baza
del kandydatki[0]
wynik.append(baza)
print wynik
Nowe rzeczy w tym przykładzie...
Przed przystąpieniem do wyjaśnienia tajemnic indeksowania elementów listy krótko objaśnię przykład. Jest to wersja starożytnego algorytmu zwanego "Sitem Erastotenesa". (lub coś blisko tego). Bierze on pod uwagę zestaw (lub w tym przypadku listę) kandydujących liczb, a następnie systematycznie usuwa liczby, o których wiadomo, że nie są liczbami pierwszymi. Skąd o tym wiadomo? Ponieważ są one produktem dwóch innych liczb. Rozpoczynamy listą kandydatek zawierającą liczby [2..999] - wiemy, że 1
jest liczbą pierwszą (właściwie, może być lub nie, różne są na ten temat
opinie), i chcemy otrzymać wszystkie liczby pierwsze poniżej
1000. (Właściwie, nasza lista kandydatek zawiera liczby w przedziale
[3..999], jednak 2 jest również kandydatką, ponieważ jest naszą pierwszą
Rzeczy do przemyślenia: Co wyjątkowego jest podczas pierwszego
wykonania tego algorytmu? Czy 2, która jest tutaj bazą, jest również
usuwana w procesie "przesiewania"?
Dlaczego? Dlaczego coś takiego nie dzieje się z innymi liczbami bazowymi?
Czy możemy być pewni, że Teraz - no właśnie co dalej? Ach tak, indeksowanie. I podział list.
Są to sposoby, aby dostać się do indywidualnych elementów listy. Widziałeś już
w akcji zwykłe indeksowanie. Jest ono dość proste. Właściwie powiedziałem ci
już wszystko, co powinieneś wiedzieć o tym, z wyjątkiem jednej rzeczy:
Ujemne indeksy zliczają elementy od końca listy. Zatem,
Podział, jednakże, jest nowy dla ciebie. Jest on podobny do indeksowania z tym wyjątkiem, że otrzymujesz część listy, a nie tylko pojedynczy element. Jak można to zrobić? Np. tak:
jedzenie = ["ciasto","ciasto","jajka","kiełbasa","ciasto"]
print jedzenie[2:4]
# Wydrukuje "['jajka', 'kiełbasa']"
[Więcej o tym później...] 10. Więcej o abstrakcji - obiekty i programowanie orientowane
obiektowo Jak sugeruje tytuł części, programowanie orientowane obiektowo jest po prostu kolejnym sposobem skupienia szczegółów. Procedury łączą proste wyrażenia w bardziej skomplikowane operacje nadając im nazwę. W OOP, w taki sposób postępujemy z obiektami. (I co, zaskoczyło cię to?) Na przykład, jeśli stworzyliśmy program piekący sernik, zamiast pisania mnóstwa procedur odpowiedzialnych za temperaturę, czas, składniki itd., mogliśmy to wszystko skupić w obiekcie-serniku. Mogliśmy również stworzyć obiekt-piekarnik i obiekt-zegar... Rzeczy takie jak temperatura mogłyby być atrybutami obiektu-sernika, podczas gdy czas mógłby być odczytywany z obiektu-zegara. Aby nasz program robił coś, moglibyśmy nauczyć nasze obiekty kilku metod; na przykład, piekarnik mógłby wiedzieć jak piec sernik itd. Zatem - jak to zrobimy w Pythonie? Nie możemy bezpośrednio utworzyć obiektu. Zamiast budowania piekarnika, tworzymy przepis opisujący jakie są piekarniki. Ten przepis opisuje klasę (class) obiektu, który nazywamy piekarnikiem. Bardzo prosta klasa piekarnika może wyglądać tak:
class Piekarnik:
def wlozSernik(self, sernik):
self.sernik = sernik
def wyjmijSernik(self):
return self.sernik
Wygląda to dziwacznie, czy nie? Nowe rzeczy w tym przykładzie...
Domyślam się, że niektóre rzeczy są nadal niejasne w tym przykładzie.
Na przykład, czym jest to Weźmy się najpierw za ostatni punkt. Obiekt tworzy się przez wywołanie nazwy klasy, tak jakby była funkcją:
mojPiekarnik = Piekarnik()
mojSernik = Sernik()
mojPiekarnik.wlozSernik(mojSernik)
Odpowiedź do ćwiczenia 2
def euklides(a,b):
while b:
a,b = b,a % b
return a
Przypisy [1] Wirtualna Kuchnia Polska - sernik na kruchym cieście Z.Wodecki (wodecki@kki.net.pl)
Copyright © Magnus Lie Hetland (mlh@idi.ntnu.no) Translation into Polish Wojciech Warczakowski (wowar@poczta.onet.pl) Last modified: Fri Jul 14 01:48:51 MET DST 2000 |
|