Skip to content
Ridgun
English

Ridgun

The language itself: a compiler that compiles itself, the stable project tool, the specification and the book

04-eredu-bat-etortzea-eta-zainak.md208 lines · 10.9 KBRaw
1# 04. kapitulua: Agur `if/else`-ri, agur `while`-ri (Eredu Bat-etortzea, Zainak eta TCO)
2
3Norbaiti kontatzen diozunean Ridgunen **ez dela existitzen `if` hitz gakoa, ez `else`, ez `switch`, ez `for`, ez `while`**, etxe bat mailurik etailtzerik gabe eraikiko duzula esan bazenio bezala begiratu ohi dizu:
4
5> *"Ez dagoela `if` ezta `while` ere? Eta nola arraio hartzen dut erabaki bat edo zeharkatzen ditut milioi bat datu?"*
6
7Bada, kontua da `if/else` eta `while` 50eko hamarkadan jauziek (`GOTO`) funtzionatzen zuten modutik oinordetzan hartutako tresna nahiko zakarrak direla. Ridgunen eskularru bat bezala doitzen diren hiru piezarekin ordezkatzen ditugu:
81. **Eredu Bat-etortzea (*Pattern Matching*)** (datuaren **forma edo balio zehatzaren** arabera erabakitzen duzunean).
92. **`|` zain boolearrak** (**baldintza edo tarte baten** arabera erabakitzen duzunean).
103. **Isats-errekurtsioa TCOrekin** (konpiladoreak azpitik `loop` begizta natibo ultra-azkar bihurtzen duena).
11
12Ikus ditzagun martxan.
13
14---
15
16## 4.1. *Pattern Matching*: Argumentuak berak erantzuna ematen dizunean
17
18Aurreko kapituluan aurrerapen bat ikusi genuen. `x` aldagai generiko bat jaso eta funtzioaren barruan bost `if (x == ...)` ireki beharrean, Ridgunen kasuak zuzenean funtzioaren sarrerako atean jartzen dituzu:
19
20```ridgun
21http kodea Int => String
22http kodea 200 = "OK: Dena primeran joan da"
23http kodea 301 = "Betiko lekuz aldatua"
24http kodea 404 = "Ez da aurkitu (begiratu duzu sofaren azpian?)"
25http kodea 500 = "Sua zerbitzarian"
26http kodea n = "Kode ezezaguna: {n}"
27```
28
29Ridgunek errenkadak **goitik behera** probatzen ditu:
30- `404` sartzen bada, zuzenean hirugarren errenkadara egiten du jauzi.
31- `418` sartzen bada, lehenengo lau errenkadetako bat ere ez dator bat, beraz azkenekoan (`n`) erortzen da, zeinak edozein osoko harrapatzen duen eta `n` izendatzen duen inprimatu ahal izan dezazun.
32- Eta azken kasuan zein zenbaki zen ere axola ez bazaizu? `_` azpimarra bat (*komodina*) jartzen duzu eta ahaztu egiten zara: `http kodea _ = "Beste kode bat"`.
33
34---
35
36## 4.2. Zain boolearrak (`|`): Zure bisturia tarteetarako, baldintzazko aldagaietarako eta erabaki-zuhaitzetarako
37
38*"Ados, pattern matching-a bikaina da `200` edo `404` bilatzen dudanean. Baina zer egiten dut `nota >= 5` edo `adina > 18 and adina < 65` den egiaztatu nahi badut? Ez ditut 47 errenkada idatziko 18tik 65erako zenbaki guztiekin!"*
39
40Noski ezetz! Horretarako daude **zainak (`|`)**.
41
42Emaitza desberdintza baten edo konbinazio logiko baten menpe dagoenean, funtzioaren (edo edozein aldagai lokalen) `=` ikurraren ondoren `|` barra bertikalen eskailera bat irekitzen duzu:
43
44```ridgun
45kalifikatu Int => String
46kalifikatu nota =
47 | nota < 0 = "Nota ezinezkoa (puntu negatiboak?)"
48 | nota < 5 = "Gutxiegi"
49 | nota < 7 = "Nahiko"
50 | nota < 9 = "Oso ongi"
51 | nota <= 10 = "Bikain"
52 | otherwise = "Eskalatik pasa zara"
53```
54
55Begiratu ondo:
56- Goiburuko `=` ikurra (`kalifikatu nota =`) `=>` azpian lerrokatzen da beste edozein funtziotan bezala.
57- Azpian goitik behera irakurtzen da tarifa-taula bat bezalaxe: `| baldintza = emaitza` lerro bakoitza ordenan egiaztatzen da. Batek `True` ematen duen bezain laster, emaitza hori itzultzen du eta kitto.
58- Amaieran **`| otherwise = ...`** jartzen duzu, hitzez hitz *"beste edozein kasutan"* esan nahi duena (zure segurtasun-sarea, inoiz itzulerarik gabeko biderik gera ez dadin).
59
60### Logika boolearra letra xehe argietan (`and`, `or`, `xor`, `not`)
61Konturatu zara lengoaia askok bitetarako `|`, boolearretarako `||`, ukatzeko `!`... erabiltzen dituztela eta baldintza konplexu bat irakurtzen duzunean katu bat teklatuaren gainetik ibili dela dirudiela (`if (!a && (b || c))`)?
62
63Ridgunen, `|` dagoeneko zainen eta datu-mota aljebraikoen ikur sakratua denez, eta `||` paralelismo-eragilea denez, **eragile boolearrak letra xeheko hitz argiekin idazten dira**:
64
65- **`and`**: bi gauzak betetzen dira zirkuitulaburreko ebaluazioarekin (`adina >= 18 and gidabaimena_du`).
66- **`or`**: gutxienez bat betetzen da zirkuitulaburreko ebaluazioarekin (`eguna == "larunbata" or eguna == "igandea"`).
67- **`xor`**: bata edo bestea betetzen da, baina ez biak batera.
68- **`not`**: boolearra alderantzikatzen du (`not (hutsik dago testua)`).
69- **`<`, `>`, `<=`, `>=`**: zenbakiak (`Int`, `Float`) zein **`String` kateak ordena lexikografikoan (UTF-8)** alderatzen dituzte (`"alfa" < "beta"`).
70
71Begira zein garbi geratzen den bisurte-urteen problema klasikoa:
72
73```ridgun
74bisurtea da Int => Bool
75bisurtea da u =
76 | (u % 400) == 0 = True
77 | (u % 4) == 0 and (u % 100) != 0 = True
78 | otherwise = False
79```
80
81Zero ikur kriptiko. Ridgun ez dakien norbaitek irakurtzen du eta lehenengoan ulertzen du.
82
83### Baldintzazko aldagai lokalak (`x =` + `|`)
84Ridgunen `=` orok arau bera jarraitzen duenez, **`|` aldagai lokal bat esleitzeko ere erabil dezakezu**, aparteko funtzio laguntzaile bat sortu beharrik izan gabe:
85
86```ridgun
87txartel prezioa Int Bool => Int
88txartel prezioa oinarria familia_ugaria_da =
89 beherapena =
90 | familia_ugaria_da = 20
91 | oinarria > 100 = 10
92 | otherwise = 0
93 oinarria - (oinarria * beherapena / 100)
94```
95
96### Habiaratutako erabaki-zuhaitzak eta `|.` mugatzaile trinkoa
97*"Eta `| cond =` edo `| otherwise =` adar baten barruan 3 edo 4 sakonera-mailako beste `|` zain-azpizuhaitz bat ireki nahi badut eta gero goiko maila batera itzuli?"*
98
99`|` barruan `|` habiaratu dezakezu nahi adina mailatan. Eta habiaratutako `|` bloke bat amaitzen denean baina azpian guraso-maila bateko adar gehiago (edo blokeko sententzia gehiago) jarraitzen dutenean, **`|.`** jartzen duzu bere azken lerroaren amaieran (`| otherwise = balioa |.`) inolako anbiguotasunik gabe adierazteko azpizuhaitz hori ixten dela eta aurreko mailara itzultzen zarela:
100
101```ridgun
102bideratu eskaera Int Int Bool Bool Int Int => Int
103bideratu eskaera metodoa rola sinatua admin_da tamaina_kb mugatua =
104 | metodoa == 0 =
105 | tamaina_kb == 0 = 204
106 | otherwise = 200 |.
107 | metodoa == 1 =
108 | not sinatua = 401
109 | otherwise =
110 -- 2. maila: POST eskaera sinatua
111 | mugatua > 0 =
112 -- 3. maila: rate-limit kontrola
113 | admin_da =
114 -- 4. maila: salbuespena administratzaileentzat
115 | tamaina_kb > 10000 = 413
116 | otherwise = 202 |.
117 | otherwise = 429 |.
118 | tamaina_kb > 1000 =
119 -- 3. maila: 4. mailatik itzuli gara `|.`-ri esker
120 | rola >= 2 and admin_da = 201
121 | otherwise = 413 |.
122 | otherwise = 201 |.
123 | otherwise = 405
124```
125
126Erreparatu zein garbia den:
127- Maila 1eko zuhaitz sinple batean (edo funtzio osoaren amaieran) **ez da inoiz `|.` jarri behar** (beharrezkoa ez den zarata litzateke).
128- Habiaratutako adar bat ixtean soilik idazten duzu `|.`, azpian goiko maila bateko adar batek jarraitzen duenean.
129
130### Lerro anitzeko blokeak zain baten barruan (`| cond =` + koskadun blokea)
131Funtzio arrunt baten `=` ikurraren ondoren lerro anitzeko koskadun bloke bat ireki dezakezun bezalaxe, **edozein `| cond =` edo `| otherwise =` zainen `=` ikurraren ondoren ere lerro-jauzi bat egin dezakezu eta bloke sekuentzial bat ireki**:
132
133```ridgun
134doitu leihoa Int Int => Int
135doitu leihoa idx muga =
136 | idx >= muga =
137 goia = muga - 1
138 eskalatua = goia * 2
139 eskalatua + 10
140 | otherwise =
141 hurrengoa = idx + 1
142 hurrengoa * 2
143```
144
145Zainaren adar bakoitzak bere bloke lokal garbia du eta bere azken lerroa adar horrek itzultzen duen balioa da.
146
147---
148
149## 4.3. "`while` gabe? Eta ez al da pila lehertuko fitxategi handi bat zeharkatzen badut?"
150
151Hitz egin dezagun gelan dagoen elefanteaz.
152
153Python, Java edo JavaScriptetik bazatoz, txiki-txikitatik irakatsi dizute *"errekurtsioa dotorea da unibertsitateko arbelean, baina mundu errealean pila gainezkatzen du (`StackOverflowError`) 1.000 biratan; erabili `while` begizta bat"*.
154
155Hori egia da isats-deiak optimizatzen **ez** dituzten lengoaietan. Baina Ridgun bezalako sistemetako lengoaia funtzional batean, **isats-errekurtsio bat funtzio puruz mozorrotutako `while` begizta bat DA**.
156
157### Zein alde dago errekurtsio arrunt baten eta isats-errekurtsio baten artean?
158Erreparatu `n`-tik `0`-ra atzeraka zenbatuz zenbakiak batzen dituzten bi funtzio hauei:
159
160```ridgun
161-- A bertsioa: Errekurtsio ARRUNTA (itzulerarako lana zintzilik uzten du)
162batu motel Int => Int
163batu motel 0 = 0
164batu motel n = n + (batu motel (n - 1))
165
166-- B bertsioa: ISATS-errekurtsioa (kontua egunean darama `metatua` aldagaian)
167batu azkar loop Int Int => Int
168batu azkar loop 0 metatua = metatua
169batu azkar loop n metatua = batu azkar loop (n - 1) (metatua + n)
170```
171
172- **A bertsioan**, `n + ...` batu ahal izan aurretik, funtzioak `batu motel (n - 1)` amaitu arte itxaron behar du. Horrek ordenagailua urrats bakoitzean `n` pilan gordetzera behartzen du.
173- **B bertsioan**, `batu azkar loop`-erako deia **lerroan gertatzen den azken gauza da**. Ez da ezer zintzilik geratzen haren atzean. Batura eguneratua pasatzen diozu dagoeneko `metatua + n`-n.
174
175### `ridgunc`-en magia-trikimailua (TCO: *Tail-Call Optimization*)
176`ridgunc`-ek `batu azkar loop` konpilatzen duenean, funtzioak klausularen amaieran bere buruari deitzen diola ikusten du eta honela dio:
177
178> *"Zertarako sortuko dut pila-marko berri bat oraingoak dagoeneko ezertarako balio ez badu? `n` gainidatziko dut `n - 1`-ekin, `metatua` gainidatziko dut `metatua + n`-rekin eta hasierara jauzi egingo dut `loop { ... continue; }` natibo batekin."*
179
180Emaitza:
1811. `batu azkar loop 100_000_000 0` (ehun milioi bira) deitu dezakezu eta **zehazki 0 pila-byte gehigarri gastatuko ditu**.
1822. Rust edo C-n eskuz idatzitako `while` begizta bat bezain azkar doa, baina zure Ridgun kodean aldagai aldakor bakar bat ere deklaratu behar izan gabe.
183
184---
185
186## 4.4. Ridgun programa orotan erabiliko duzun eredu maisua
187
188Beste lengoaia batean *"`while` begizta bat egingo dut `i` indize batekin eta metatzaile batekin"* pentsatuko zenukeen bakoitzean, Ridgunen honelako funtzio-bikote bat idazten duzu (funtzio nagusi garbia eta bere isatseko `_loop` laguntzailea):
189
190```ridgun
191bilatu karakterea loop String Int Int Int => Int
192bilatu karakterea loop testua pos ama bilatua =
193 | pos >= ama = -1
194 | (str char at testua pos) == bilatua = pos
195 | otherwise = bilatu karakterea loop testua (pos + 1) ama bilatua
196
197bilatu karakterea String Int => Int
198bilatu karakterea testua bilatua = bilatu karakterea loop testua 0 (str len testua) bilatua
199```
200
201Begira `bilatu karakterea loop`-en hiru zainei:
2021. Amaierara iritsi naiz (`pos >= ama`)? `-1` itzultzen dut (begiztaren amaiera).
2032. Karaktere hori `pos`-en aurkitu dut? `pos` itzultzen dut (begiztaren amaiera).
2043. Ez da hau? Hurrengora pasatzen naiz (`pos + 1`) isats-dei batekin (konpiladoreak `continue;` bihurtzen duena).
205
206Garbia, zapaldutako aldagai globalen *bug*-ak edukitzea ezinezkoa eta tximista bezain azkarra.
207
208**05. kapituluan** datu-bildumei helduko diegu: **zerrendak, tuplak, argumentu infinituak (`...`) onartzen dituzten funtzioak eta J/APL estiloko sekuentzia-maskarak**.