Pennod 02: Strwythurau Data a Ffwythiannau

Bydd y daflen lab yma yn symud ymlaen i ddeall sut i ysgrifennu ffwythiannau (yn debyg iawn i ffwythiannau mathemategol) a rhestrau (ffordd o gynnal data).

Tiwtorial

Gweithiwch trwy'r canlynol:


1. Rhestrau

Fideo yn disgrifio'r cysyniad.

Fideo demo.

Math arall o newidyn yn Python (rydym yn barod wedi gweld newidynnau rhifiadol a newidynnau o gymeriadau) yw rhestr. Mae hwn yn ymddwyn fel cynhwysydd sy'n dal nifer o eitemau arall mewn trefn.

Dyma restr o'm hoff rifau:


In [1]:
hoff_rhifau = [9, 12, 13, 7]
hoff_rhifau


Out[1]:
[9, 12, 13, 7]

In [2]:
type(hoff_rhifau)


Out[2]:
list

Gallwn wneud amryw o bethau i eitemau mewn rhestr:


In [3]:
sum(hoff_rhifau)  # Adio'r holl elfennau yn y rhestr


Out[3]:
41

In [4]:
max(hoff_rhifau)  # Cael yr elfen mwyaf yn y rhestr


Out[4]:
13

In [5]:
min(hoff_rhifau)  # Cael yr elfen lleiaf yn y rhestr


Out[5]:
7

In [6]:
hoff_rhifau.append(-100)  # Ychwanegu elfen arall i'r rhestr
hoff_rhifau


Out[6]:
[9, 12, 13, 7, -100]

Hefyd gallwn adfer eitemau penodol o'r rhestr. Mae hwn yn gweithio yn yr un modd â gyda strings:


In [7]:
hoff_rhifau[0]  # Cael elfen cyntaf y rhestr


Out[7]:
9

In [8]:
hoff_rhifau[1]  # Cael ail elfen y rhestr


Out[8]:
12

In [9]:
hoff_rhifau[-1]  # Cael elfen olaf y rhestr


Out[9]:
-100

In [10]:
hoff_rhifau[2:4]  # Cael y 3ydd i'r 4ydd elfen y rhestr


Out[10]:
[13, 7]

Mae strings (gweler y daflen lab diwethaf) a rhestrau yn debyg iawn yn y ffordd maent yn 'cynwysyddion' ar gyfer eitemau. Mae lot o'r gweithrediadau sy'n gweithio gyda rhestrau hefyd yn gweithio gyda strings:


In [11]:
enwcyntaf = "Vince"
len(enwcyntaf)  # Faint o gymeriadau sydd yn y newidyn


Out[11]:
5

In [12]:
enwcyntaf[0]  # Gallwn pwyntio at cymeriadau unigol, 0 yw'r cyntaf


Out[12]:
'V'

In [13]:
enwcyntaf[4]


Out[13]:
'e'

In [14]:
enwcyntaf[-1]  # Gallwn defnyddio rhifau negatif i dechau'r cyfri o'r diwedd


Out[14]:
'e'

In [15]:
enwcyntaf[0:4]  # Gallwn 'sleisio' strings


Out[15]:
'Vinc'

Arbrofwch trwy creu rhestrau a'u trin.

2. Lwpiau 'for'.

Fideo yn disgrifio'r cysyniad.

Fideo demo.

Yn y daflen lab diwethaf, gwelwn gallwn ddefnyddio lwp while i ailadrodd darn o god nes bod amod boolean yn False. Mae hefyd yn bosib ailadrodd rhyw weithrediad ar gyfer pob elfen mewn rhestr.


In [16]:
eitemmau = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # Creu rhestr
cyfanswm = 0
for eitem in eitemmau:
    cyfanswm += eitem
cyfanswm


Out[16]:
55

Ffordd gyflym o gael rhestr o gyfanrifau yw'r gorchymyn range:


In [17]:
for k in range(10):
    print(k)


0
1
2
3
4
5
6
7
8
9

Arbrofwch trwy symio (neu efallai lluosi?) dros wahanol restrau o eitemau.

3. Cyfansoddiad rhestrau

Fideo yn disgrifio'r cysyniad.

Fideo demo.

Dyma restr o rifau sgwâr, wedi'i gynhyrchu trwy ddefnyddio'r dull append a lwp 'for'.


In [18]:
cyfanswm = 0
hoff_rhifau_sgwar = []  # Creu rhestr gwag
for n in hoff_rhifau:
    hoff_rhifau_sgwar.append(n ** 2)
hoff_rhifau_sgwar


Out[18]:
[81, 144, 169, 49, 10000]

Ond gallwn ni gwneud hwn trwy ddefnyddio cystrawen Python o'r enw cyfansoddiad rhestrau:


In [19]:
hoff_rhifau_sgwar = [x ** 2 for x in hoff_rhifau]
hoff_rhifau_sgwar


Out[19]:
[81, 144, 169, 49, 10000]

Mae hwn yn gyfarwydd, gan ei fod yn dyblygu nodiant mathemategol. Er enghraifft dyma sut gallwn gael swm yr elfennau yn y set ganlynol:

$$ \{n \in S \;| \text{ os yw } n \text{ yn rhanadwy gan 2}\} $$

(Dyma nodiant mathemategol ar gyfer "y set o'r holl bethau yn $S$ sy'n rhanadwy gan 2".)


In [20]:
sum([n for n in hoff_rhifau if n % 2 == 0])


Out[20]:
-88

Arbrofwch trwy newid y cod yma i greu rhestr wahanol.

4. Ysgrifennu ffwythiannau syml.

Fideo yn disgrifio'r cysyniad.

Fideo demo.

Yn aml bydd angen defnyddio cod mwy nag unwaith. Y ffordd o wneud hwn yw ysgrifennu ffwythiant. Dyma enghraifft syml. Mae'n ffwythiant sy'n allbynnu string yn dweud "Helo byd":


In [21]:
def dweud_hi():
    return "Helo byd"

Nawr, i ddefnyddio'r ffwythiant mae angen galw'r ffwythiant, gallwn wneud hwn trwy ddefnyddio pâr o gromfachau ().


In [22]:
dweud_hi()


Out[22]:
'Helo byd'

Mae'n ymarfer da i dorri cod lawr i ffwythiannau bach er mwyn i'w wneud yn haws i'w ddarllen.

Arbrofwch gyda newid beth mae'r ffwythiant dweud_hi yn dweud.

5. Ffwythiannau gyda newidynnau.

Fideo yn disgrifio'r cysyniad.

Fideo demo.

Mae'n fwy defnyddio i gynnwys newidynnau yn ein ffwythiannau (yn yr union un modd a ffwythiannau mathemategol!).

Gadewch i ni ail-edrych ar y ffwythiant mathemategol disgrifion ni yn y daflen lab diwethaf:

$$ f(n)=\begin{cases} 1&\text{ os yw } n\leq 5\\ 2&\text{ os yw } n> 5\text{ ac } n \text{ yn eilrif }\\ 3&\text{ fel arall }\\ \end{cases} $$

Dyma'r cod sy'n diffinio'r ffwythiant yma (cymharwch hwn gyda'r cod ysgrifennon ni yn y daflen lab diwethaf):


In [23]:
def f(n):
    """
    Fe elwir yr ysgrifen sydd rhwng dyfynodau triphlyg yn 'doc string'.
    Defnyddiwn hwn i ddisgrifio beth mae'r ffwythiant yn gwneud. Er
    enghraifft byddwn yn ysgrifennu: Mae'r ffwythiant yma yn allbynnu
    f(n) fel y disgrifir uchod.
    """
    if n <= 5:
        return 1
    elif n % 2 == 0:  # fel arall, os
        return 2
    else:  # fel arall
        return 3
f(11)


Out[23]:
3

Gallwn hefyd cael ffwythiannau gyda mwy nag un newidyn:


In [24]:
def swm_syml(a, b):
    """Allbynu swm a ac b"""
    return a + b
swm_syml(5, 7)


Out[24]:
12

Mae hefyd yn bosib gosod newidynnau diofyn:


In [25]:
def swm_syml(a=5, b=1):
    """Allbynu swm a ac b"""
    return a + b

In [26]:
swm_syml()


Out[26]:
6

In [27]:
swm_syml(b=2)


Out[27]:
7

In [28]:
swm_syml(a=3)


Out[28]:
4

Arbrofwch trwy greu ffwythiant eich hun


Enghraifft weithredol

Fideo yn disgrifio'r cysyniad.

Fideo demo.

Diffinnir y rhifau Fibonacci gan y fformiwla fathemategol:

$$ f_n = \begin{cases} 1,\text{ is yw } n \in \{0, 1\}\\ f_{n - 1} + f_{n - 2}, \text{ fel arall } \end{cases} $$

Nod y cwestiwn yma yw gwirio'r theorem ganlynol am swm y rhifau Fibonacci cyntaf:

$$ \sum_{i=0}^n f_i = f_{n + 2} - 1 $$

Fel enghraifft gyda $n=2$ mae gennym $f_0=f_1=1, f_2=2, f_3=3$ ac $f_5=8$. Felly mae gennym:

$$ \sum_{i=0}^nf_i = 1 + 1 + 2 + 3 = 7 = 8 - 1 = f_{3 + 2} - 1 $$

Byddwn yn gwirio'r fformiwla trwy ddefnyddio Python. Yn gyntaf ysgrifennwn ffwythiant ar gyfer y dilyniant Fibonacci ei hun:


In [29]:
def fibonacci(n):
    """Allbynnu'r n fed rhif fibonacci"""
    if n in [0, 1]:  # Yr achos arbennig o n for yn 0 neu 1
        return 1
    a = 1  # Gosod y gwerthoedd cychwynnol
    b = 1
    for i in range(n - 1):  # Yn mynd trwy;r n - 1 term cyntaf
        drosdro = b  # Newidyn dros dro i cofio gweth b
        b = a + b  # Gwerth newydd b
        a = drosdro  # Gwerth newydd a (hen werth b)
    return b

Gadewch i ni alw'r ffwythiant yma i wirio ei fod yn gywir:


In [30]:
for n in range(5):
    print(fibonacci(n))


1
1
2
3
5

Gallwn nawr cael rhestr o'r $K$ rhif fibonacci cyntaf a gwirio'r theorem:


In [31]:
K = 3
rhestr_fib = [fibonacci(n) for n in range(K + 1)]
sum(rhestr_fib) == fibonacci(K + 2) - 1


Out[31]:
True

Gallwn nawr rhoi'r cod yma, sy'n gwirio os yw'r perthynas yn wir, i mewn i ffwythiant newydd:


In [32]:
def gwirio_theorem(K):
    """
    Gwirio'r perthynas ar gyfer swm y K rhif fibonacci cyntaf
    """
    rhestr_fib = [fibonacci(n) for n in range(K + 1)]
    return  sum(rhestr_fib) == fibonacci(K + 2) - 1

Nawr gwiriwn ni'r theorem ar gyfer y 200 gwerth cyntaf $K$:


In [33]:
gwiriadau = [gwirio_theorem(K) for K in range(200)]
all(gwiriadau)  # Mae `all` yn cyfuno pob boolean mewn rhestr


Out[33]:
True

Ymarferion

Dyma nifer o ymarferion sy'n bosib cario allan trwy ddefnyddio'r cysyniadau a thrafodwyd:

  • Rhestrau, er enghraifft: rhifau_od = [1, 3, 5, 7]
  • Lwpiau 'for', er enghraifft:
    for i in rhifau_od:
          print(i ** 2)
    
  • Cyfansoddiadau rhestrau, er enghraifft: rhifau_od_sgwar = [n ** 2 for n in rhifau_od]
  • Ffwythiannau, er enghraifft:
    def rhannu(a=4, b=1):
          return a / b
    

Ymarfer 1

Ymarfer datbygio

Mae'r cod canlynol yn ceisio ysgrifennu $n!$ fel ffwythiant. Canfyddwch a chywirwch yr holl bygs.

def ffactorial(n):
       """Ffwythiant sy'n allbynnu ffactorial n"""
       for i in range(n)
           lluoswm *= i

In [34]:
def ffactorial(n):
    """Ffwythiant sy'n allbynnu ffactorial n"""
    lluoswm = 1  # Angen cychwyn y newidyn ffug yma.
    #for i in range(n)  # Mae `:` ar goll, nid yw'r amrediad yn gywir
    for i in range(1, n + 1):
        lluoswm *= i
    return lluoswm  # Nid oedd yn allbynnu

Gwiriad gloi:


In [35]:
for n in range(5):
    print(n, ffactorial(n))


0 1
1 1
2 2
3 6
4 24

Ymarfer 2

Ysgrifennwch ffwythiant sy'n allbynnu'r rhifau triongl $T_n$:

$$ T_n=\frac{n(n+1)}{2} $$

In [36]:
def trionglog(n):
    """Allbynnu'r nfed rhif triongl"""
    return n * (n + 1) / 2

In [37]:
for n in range(10):
    print(trionglog(n))


0.0
1.0
3.0
6.0
10.0
15.0
21.0
28.0
36.0
45.0

Ymarfer 3

Defnyddiwch god i wirio fod y perthynas yma yn wir:

$$ \sum_{i=0}^{n}T_i=\frac{n(n+1)(n+2)}{6} $$

In [38]:
def gwirio_theorem(K):
    """
    Gwirio'r perthynas ar gyfer swm y K rhif triongl cyntaf
    """
    rhestr_trionglog = [trionglog(n) for n in range(K + 1)]
    return  sum(rhestr_trionglog) == K * (K + 1) * (K + 2) / 6

In [39]:
gwirio_theorem(4)


Out[39]:
True

Fan hyn gwiriwn y 200 rhif cyntaf:


In [40]:
gwiriadau = [gwirio_theorem(K) for K in range(200)]
all(gwiriadau)  # Mae `all` yn cyfuno holl boolean mewn rhestr


Out[40]:
True

Ymarfer 4

Crëwch restr gyda'r 1300 cyfanrif cyntaf sy'n rhanadwy â 3. Beth yw'r rhif mwyaf yma? Beth yw'r rhif lleiaf? Beth yw cymedr y rhifau yma?


In [41]:
rhifau_rhanadwy = []
terfan_uchaf = 1300
n = 0
while len(rhifau_rhanadwy) < terfan_uchaf:
    n += 1
    if n % 3 == 0:
        rhifau_rhanadwy.append(n)

In [42]:
max(rhifau_rhanadwy)  # rhif mwyaf


Out[42]:
3900

In [43]:
min(rhifau_rhanadwy)  # rhif lleiaf


Out[43]:
3

In [44]:
sum(rhifau_rhanadwy) / len(rhifau_rhanadwy)  # cymedr


Out[44]:
1951.5

Ymarfer 5

Archwiliwch i mewn i ddefnydd y llyfrgell Python random. Defnyddiwch hwn i efelychi'r problem Monty hall:

  • Ysgrifennwch ffwythiant sy'n efelychu'r gem pan rydych yn cadw at eich dewis gwreiddiol.
  • Ysgrifennwch ffwythiant sy'n efelychu'r gem pan rydych yn newid eich dewis.
  • Ailadroddwch chwarae'r em gan ddefnyddio'r ddau ffwythiant a chymharwch y tebygolrwydd o ennill.

In [45]:
import random  # mewnforio'r llyfrgell random

In [46]:
random.random()  # Mae hwn yn rhoi haprif rhwng 0 ac 1


Out[46]:
0.38665853758464197

Nodwch nad yw'r rhain yn haprifau go iawn: mewn gwirionedd maent yn dilyn dilyniant nad yw'n hawdd rhagfynegi ac sy'n ymddwyn yn debyg iawn i haprifau. Mae'n bosib nodi rydym eisiau'r union un dilyniant, ac rydym yn gwneud hwn trwy osod hedyn:


In [47]:
random.seed(1)  # Cymerir hwn unrhyw cyfanrif
random.random(), random.random()


Out[47]:
(0.13436424411240122, 0.8474337369372327)

Gadewch i ni alw'r cod uchod unwaith eto:


In [48]:
random.seed(1)  # Defnyddiwn yr un hedyn ac o'r blaen
random.random(), random.random()  # A chawn yr un rhifau


Out[48]:
(0.13436424411240122, 0.8474337369372327)

Gallwn hefyd gwneud hapddewis o restr:


In [49]:
drysau = ['A', 'B', 'C']
random.choice(drysau)


Out[49]:
'A'

In [50]:
def cadw():
    """Ffwythiant sy'n efelychu gem lle rydym yn cadw at in dewis"""
    ennill = 0 

    drysau = ['Car', 'Gafr', 'Gafr']
        
    dewis_cychwynnol = random.choice(drysau)  # gwneud dewis
    return dewis_cychwynnol == 'Car'

In [51]:
cadw()


Out[51]:
False

In [52]:
def newid():
    """Ffwythiant sy'n efelychu gem lle rydym yn newid ein dewis"""
    drysau = ['Car', 'Gafr', 'Gafr']
    
    dewis_cychwynnol = random.choice(drysau)  # gwneud dewis
    drysau.remove(dewis_cychwynnol)  # Newid y dewis cychwynnol
    drysau.remove('Gafr')  # Mae'r cyflwynydd yn dangos gafr
    
    dewis_newydd = drysau[0]  # Dewisiwn yr un opsiwn sydd ar ôl
            
    return dewis_newydd == 'Car'

In [53]:
newid()


Out[53]:
False

Nawr rhedwn nifer o ailadroddiadau o'r uchod er mwyn cael y tebygolrwydd o ennill:


In [54]:
random.seed(0)
ailadroddiadau = 10000
tebygolrwydd_ennill_cadw = sum([cadw() for ail in range(ailadroddiadau)]) / ailadroddiadau
tebygolrwydd_ennill_newid = sum([newid() for ail in range(ailadroddiadau)]) / ailadroddiadau
tebygolrwydd_ennill_cadw, tebygolrwydd_ennill_newid


Out[54]:
(0.3346, 0.6649)

Ymarfer 6

Un math o ddata nad ydym wedi ystyried yw geiriaduron. Mae'r rhain yn fath penodol o rywbeth a elwir yn 'dabl stwnsh' (hash table). Canfyddwch wybodaeth am eiriaduron ac arbrofwch gyda nhw.

Mae geiriaduron yn strwythurau data sy'n gadael i ni fapio allweddau i werthoedd. Mae hwn yn ddefnyddiol pan mae angen cael un set o werthoedd o set arall. I bob pwrpas hwn yw ffordd o raglennu ffwythiant deusaethol.

Fel enghraifft byddwn ni eisiau strwythur data sy'n mapio enwau pobl i'w rhifau myfyrwyr:


In [55]:
enwau_i_rhifau = {"Vince": 1, "Zoe": 2, "Julien": 3, "Marie": 4}
enwau_i_rhifau


Out[55]:
{'Vince': 1, 'Zoe': 2, 'Julien': 3, 'Marie': 4}

Nawr medrwn gael gwerth unrhyw allwedd benodol:


In [56]:
enwau_i_rhifau['Marie']


Out[56]:
4

Gallwn newid y gwerthoedd fel o'r blaen:


In [57]:
enwau_i_rhifau['Marie'] += 1
enwau_i_rhifau


Out[57]:
{'Vince': 1, 'Zoe': 2, 'Julien': 3, 'Marie': 5}

Os ceisiwn gael allwedd nad yw o fewn y geiriadur cawn wall, ond gallwn ychwanegu parau allwedd/gwerth i eiriaduron:


In [58]:
enwau_i_rhifau["Jon"] = 5
enwau_i_rhifau


Out[58]:
{'Vince': 1, 'Zoe': 2, 'Julien': 3, 'Marie': 5, 'Jon': 5}

In [ ]: