Liste su važan dio Sage-a i pythona. Srodne su poljima (array) iz standardnih programskih jezika (C, Fortran), ali imaju bitno više svojstava. Elementi liste mogu biti praktički bilo koji objekti.
{{{id=1| t1 = [1, 3, 5, 7, 9, 11]; print t1 t2 = [sin, cos, log]; print t2 t3 = [[], ["jedan"], t1]; t3 /// }}}Već smo ranije upoznali pristupanje elementima liste pomoću indeksiranja i mogućnost da na taj način promijenimo pojedine elemente liste:
{{{id=3| print t1[1] t1[1] = "tri" print t1[1] /// }}}Ukoliko želimo pristupiti većem broju elemenata od koristi su tzv. rezovi liste (engl. slice). Općenita sintaksa je lista[početak:kraj:korak], gdje je "početak" uključen u rez, a "kraj" nije:
{{{id=4| print t1[2:6] t1[2:6:2] /// }}}Ukoliko rez ide od početka ili do kraja liste, odgovarajuće indekse možemo izostaviti:
{{{id=75| t1[2:] /// }}}Negativni indeksi nam omogućuju da brojimo od kraja liste ...
{{{id=78| t1[-2:] # zadnja dva elementa /// }}}... a negativan korak da rez ide unatrag:
{{{id=79| t1[::-1] # lista natraške /// }}}Za traženje indeksa nekog elementa liste, ubacivanje elemenata u liste i slične operacije stoje na raspolaganju metode lista:
{{{id=5| dir(t1)[-9:] /// }}}Ovdje smo koristili funkciju dir() koja daje listu atributa nekog objekta (uključujući njegove metode). Samo zadnjih 9 je trenutno zanimljivo. (Inaće, dir() bez argumenta daje popis svih trenutno definiranih imena.)
{{{id=94| print len(dir()) dir()[:12] /// }}}Osim count() i index(), sve ostale metode mijenjaju listu.
{{{id=6| t2 = t2[:-1]; t2 # micanje zadnjeg elementa /// }}} {{{id=84| t1 /// }}}♦ Zadatak 3.1.1:Izbrišite u gornjoj listi t1 treći element (ne element s indeksom=3!) i dodajte na kraj još dva elementa, brojeve 13 i 15. Ispišite novonastalu listu, a zatim element 'tri' zamijenite s brojem 3, koristeći metodu index().
Ukoliko treba transformirati sve elemente liste, najelegantnije je koristiti obuhvaćanje liste (engl. list comprehension):
{{{id=91| [n+1 for n in t1] /// }}}Često se javlja potreba za izborom elemenata liste koji zadovoljavaju neki kriterij. To se može izvesti obuhvaćanjem liste uz dodatni uvijet:
{{{id=10| t5 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [n for n in t5 if is_even(n)] # izbor parnih elemenata /// }}}Ovdje smo koristili funkciju is_even() koje je jedna iz velike porodice tzv. predikata. Predikat je termin iz matematičke logike koji označava funkciju koja poprima isključivo vrijednosti True ili False. Većina predikata definiranih u Sageu počinje s "is_" (jer odgovaraju na pitanje "da li je ..."). Ispišimo ih (koristimo činjenicu da su stringovi kao liste znakova pa možemo koristiti rezove na njima):
{{{id=93| [p for p in dir() if p[:3]=='is_'] /// }}}Ovakve liste su pogodne za simboličke račune, ali za numeriku su efikasnija numpy polja koja smo već dosta koristili u prošlom poglavlju. Ona su zapisana u neprekinutom slijedu memorijskih lokacija što omogućuje brži pristup no zbog toga je nužno unaprijed deklarirati koji tip objekata su elementi (integer, float, complex ...) i svi elementi moraju biti istog tipa.
{{{id=12| import numpy as np # uvođenje skraćenice za ime modula /// }}} {{{id=13| a = np.array(t5); print a # konverzija obične liste u numpy polje b = np.array([2., 2.5, 3, 3.5]); print b /// }}}Očitavanje tipa objekata u numpy polju:
{{{id=98| print a.dtype b.dtype /// }}}Pri konstrukciji numpy polja izbor tipa objekata je automatski, ali moguće ga je i odrediti opcionalnim argumentom dtype:
{{{id=99| c = np.array(t5, dtype=np.float64); print c /// }}}Numpy polja imaju dosta više metoda od običnih lista:
{{{id=14| dir(a)[-66:] /// }}}(No zbog efikasnosti implementacije nema upravo onih metoda koje imaju obične liste. To je zato jer je npr. umetanje elementa u listu vrlo "skupa" operacija koja uključuje brisanje i pisanje svih elemenata koji u memoriji dolaze nakon tog mjesta umetanja. Ako želimo raditi takve stvari upotreba numpy polja nam ionako neće donijeti nikakve prednosti i treba koristiti obične liste.).
{{{id=18| b=a.reshape(2,5); print b /// }}} {{{id=19| b.shape # "oblik" polja - broj redaka i stupaca /// }}}Pristupanje pojedinom elementu višedimenzionalnog polja je moguće očitim indeksiranjem a[i][j]..., ali je efikasnije koristiti skraćeno indeksiranje: a[i, j, ...]:
{{{id=15| b[1,1] = 42. /// }}} {{{id=17| print b /// }}} {{{id=21| print a /// }}}Pažnja: radi efikasnosti, rezovi kroz numpy polja ne kopiraju originalne elemente već samo daju pokazivače na ista mjesta u memoriji (vidi gore element '42' koji se pojavio i u listi a). Obične liste nemaju takvo ponašanje:
{{{id=22| t5 = t1[2:4]; t5 /// }}} {{{id=24| t5[0] = 'novi'; print t5 t1 /// }}}Za daljnje detalje o numpy poljima, vidi ovdje.
Za konstrukciju liste cijelih brojeva najkorisnija je funkcija range():
{{{id=25| range(-5, 15, 3) /// }}}range() radi samo sa cijelim brojevima ...
{{{id=26| range(2., 7.5, 1.5) /// }}}... pa ukoliko trebamo liste realnih (float) brojeva, možemo ili dijeliti ove cijele s nekom odabranom realnom konstantom, ili koristiti numpy inačicu funkcije range() koja se zove arange() ...
{{{id=28| np.arange(2., 9.9, 1.1) /// }}}... koju po potrebi možemo i konvertirati natrag u običnu listu funkcijom list():
{{{id=30| list(_) /// }}}Kombiniranjem funkcije range() i idioma obuhvaćanja liste možemo kreirati kompleksne stvari. Npr. lista simboličkih izraza:
{{{id=31| [x^n/factorial(n) for n in range(1,6)] /// }}}♦ Zadatak 3.1.2:Konstruirajte listu svih prim-brojeva manjih od 100. Koliko ima prim-brojeva manjih od $10^4, 10^5, 10^6, \ldots$? Usporedite rezultat (i brzinu njegovog dobivanja) s ugrađenom funkcijom prime_pi(), te slijedećom aproksimacijom (koja je sadržaj slavnog teorema o prim-brojevima)
$$
\pi(x) = \int_{2}^{x} \frac{dx}{ln(x)}
$$
Inaće, mjerenje vremena potrebnog da se izvrši neka ćelija izvodi se stavljanjem "%time" u prvi red:
{{{id=119| %time factor(2923003274661805836407421649242809468366377451741) /// }}}(Prekid računa koji traje predugo izvodi se putem menija Action->Interrupt ili pritiskom na tipku "Escape".)
♦ Zadatak 3.1.3: Izračunajte funkciju $\pi(x)$ (broj prim-brojeva manjih od x) za $x=10^{10}$ statističkom metodom: Izaberite uzorak od n slučajnih cijelih brojeva između 1 i x i testirajte koliko ima prim-brojeva među njima. Iz tog udjela odredite $\pi(x)$. Kolika veličina uzorka vam treba da bi relativna greška prema prime_pi(10^10) razumno često pala ispod 1%?
Osim lista postoje i drugi "kontejneri" za objekte. Kao prvo tu su već u prošlom poglavlju spominjani tuple-ovi. Glavna razlika prema listama (osim očite razlike da liste idu u uglate, a tuplovi u okrugle zagrade) je da su tuplovi nepromjenjivi - nije moguće promijeniti neki njihov element ili im dodati nove. Ukoliko je to iz nekog razloga nužno treba konstruirati novi tupl:
{{{id=127| tpl = (1, 3, 5, 7) tpl[3] = 2 /// }}} {{{id=128| tpl[:2] + (2,) + tpl[3:] /// }}}Ali to je rijetko potrebno. Naime, osim ove razlike u promjenjivosti glavna razlika između tuplova i lista je da su tuplovi obično heterogeni strukturirani skupovi u kojima pojedina mjesta u tuplu nose različita značenja, dok su liste obično homogeni skupovi istovrsnih objekata. Npr, koordinate neke točke je prirodno staviti u tupl (x, y), a ne u listu [x,y], ali niz točaka je prirodno staviti u listu takvih tuplova [(x1, y1), (x2, y2), ...].
Daljnji kontejneri koji nam stoje na raspolaganju su skupovi (engl. set), koji su skupovi različitih(!) objekata i s kojima možemo raditi standardne stvari poput unije, presjeka, komplementa ...
{{{id=50| s1 = set([10, 9, 10, 8, 7, 7, 7, 4]); s1 /// }}}(Uočite da se duplikati automatski izbacuju.)
{{{id=53| print s1.union(l1) print s1.intersection(l1) s1.difference(l1) /// }}}Zadnji, vrlo važan, kontejner kojeg ćemo spomenuti je riječnik (engl. dictionary). Riječ je o preslikavanju key->value, gdje je value bilo koji objekt, a key može biti tipično broj ili string (premda su i druge stvari dopustive kao key, npr tuplovi).
{{{id=131| d1 = {'a':1, 'c':3, 'b':2}; print d1 d2 = {1: sin, 2: cos, 3: log}; print d2 d3 = {'ime': 'pero', 'prezime': 'peric', 'spol':'M'}; d3 /// }}}Redosljed elemenata u riječniku nema značenja. Pristup pojedinim elementima riječnika je moguć "indeksiranjem" pomoću ključa:
{{{id=139| d1['c'] /// }}}Na isti način je moguće dodavati nove elemente u riječnik:
{{{id=136| d2[5] = tan /// }}} {{{id=135| d2.keys() /// }}}Iteriranje po riječniku ne daje elemente već samo ključeve:
{{{id=141| [it for it in d2] /// }}} {{{id=134| [d2[n](1.) for n in d2.keys()] /// }}}♦ Zadatak 3.1.4: (Nema veze s riječnicima.) Pronađite najveći dvoznamenkasti broj $k$ za koji je $m=2^k - 1$ prost broj. Ispišite broj $m$ u dekadskom i binarnom sustavu:
{{{id=142| /// }}}