-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgeneratori.html
420 lines (346 loc) · 43.2 KB
/
generatori.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
<!DOCTYPE html>
<meta charset=utf-8>
<title>Chiusure & generatori - Immersione in Python 3</title>
<!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href=dip3.css>
<style>
body{counter-reset:h1 6}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=stylesheet media=print href=print.css>
<meta name=viewport content='initial-scale=1.0'>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8> <input type=search name=q size=25 placeholder="powered by Google™"> <input type=submit name=sa value=Search></div></form>
<p>Voi siete qui: <a href=index.html>Inizio</a> <span class=u>‣</span> <a href=indice.html#generatori>Immersione in Python 3</a> <span class=u>‣</span>
<p id=level>Livello di difficoltà: <span class=u title=intermedio>♦♦♦♢♢</span>
<h1>Chiusure <i class=baa>&</i> generatori</h1>
<blockquote class=q>
<p><span class=u>❝</span> La mia ortografia è Incerta. Non è una cattiva ortografia, ma non è Certa, e le lettere vanno nei posti sbagliati. <span class=u>❞</span><br>— Winnie-the-Pooh
</blockquote>
<p id=toc>
<h2 id=divingin>Immersione!</h2>
<p class=f>Ho sempre subito il fascino dei linguaggi, da degno figlio di un bibliotecario e di una laureata in letteratura inglese. Non parlo di linguaggi di programmazione. Be’ sì, parlo di linguaggi di programmazione, ma anche di linguaggi naturali. Prendete l’inglese. L’inglese è una lingua schizofrenica che prende in prestito parole dal tedesco, francese, spagnolo e latino (per nominarne alcune). In realtà, “prende a prestito” è l’espressione sbagliata; “saccheggia” è più corretto. O forse “assimila” — come i Borg. Sì, mi piace.
<p class=c><code>Noi siamo i Borg. Assimileremo le vostre peculiarità linguistiche ed etimologiche alle nostre. La resistenza è inutile.</code>
<p>In questo capitolo, imparerete qualcosa sui sostantivi plurali in inglese. E anche sulle funzioni che restituiscono altre funzioni, sull’uso avanzato delle espressioni regolari e sui generatori. Ma prima, parliamo di come si costruiscono i sostantivi plurali in inglese. (Se non avete ancora letto <a href=espressioni-regolari.html>il capitolo sulle espressioni regolari</a>, questo potrebbe essere un buon momento per farlo. Questo capitolo presume che abbiate capito le basi delle espressioni regolari, e si addentrerà molto presto nelle loro tecniche più avanzate.)
<p>Se siete cresciuti in un paese di lingua madre inglese o avete imparato l’inglese in un ambiente scolastico formale, avrete familiarità con le regole di base.
<ul>
<li>Se una parola finisce con S, X, o Z, aggiungete ES. <i>Bass</i> diventa <i>basses</i>, <i>fax</i> diventa <i>faxes</i> e <i>waltz</i> diventa <i>waltzes</i>.
<li>Se una parola finisce con una H sonora, aggiungete ES; se finisce con una H muta, aggiungete solo S. Che cos’è una H sonora? È una H che viene combinata con altre lettere per creare un suono che si possa udire. Quindi <i>coach</i> diventa <i>coaches</i> e <i>rash</i> diventa <i>rashes</i>, perché potete udire i suoni /ʧ/ e /ʃ/ quando li pronunciate. Ma <i>cheetah</i> diventa <i>cheetahs</i>, perché la H è muta.
<li>Se una parola finisce con una Y che ha un suono /i/, cambiate la Y in IES; se la Y è combinata con una vocale per ottenere un altro suono, aggiungete solo S. Così <i>vacancy</i> diventa <i>vacancies</i>, ma <i>day</i> diventa <i>days</i>.
<li>Se tutto il resto fallisce, aggiungete S e sperate per il meglio.
</ul>
<p>(Lo so, ci sono un sacco di eccezioni. <i>Man</i> diventa <i>men</i> e <i>woman</i> diventa <i>women</i>, ma <i>human</i> diventa <i>humans</i>. <i>Mouse</i> diventa <i>mice</i> e <i>louse</i> diventa <i>lice</i>, ma <i>house</i> diventa <i>houses</i>. <i>Knife</i> diventa <i>knives</i> e <i>wife</i> diventa <i>wives</i>, ma <i>lowlife</i> diventa <i>lowlifes</i>. E non fatemi parlare delle invariabili, parole che sono il loro stesso plurale come <i>sheep</i>, <i>deer</i> e <i>haiku</i>.)
<p>In altre lingue, ovviamente, le cose sono completamente differenti.
<p>Progettiamo una libreria Python per pluralizzare automaticamente i sostantivi inglesi. Cominceremo giusto con queste quattro regole, ma tenete a mente che dovrete inevitabilmente aggiungerne altre.
<p class=a>⁂
<h2 id=i-know>Ho capito, usiamo le espressioni regolari!</h2>
<p>E così state esaminando le parole, il che significa, almeno in inglese, che state esaminando stringhe di caratteri. Avete regole che dicono che dovete trovare combinazioni differenti di caratteri e poi operare su quelle combinazioni in modi differenti. Questo sembra un lavoro per le espressioni regolari!
<p class=d>[<a href=esempi/plural1.py>scarica <code>plural1.py</code></a>]
<pre class=pp><code>import re
def plural(noun):
<a> if re.search('[sxz]$', noun): <span class=u>①</span></a>
<a> return re.sub('$', 'es', noun) <span class=u>②</span></a>
elif re.search('[^aeioudgkprt]h$', noun):
return re.sub('$', 'es', noun)
elif re.search('[^aeiou]y$', noun):
return re.sub('y$', 'ies', noun)
else:
return noun + 's'</code></pre>
<ol>
<li>Questa è un’espressione regolare, ma usa una sintassi che non avete visto nel capitolo <a href=espressioni-regolari.html><i>Espressioni regolari</i></a>. Le parentesi quadre significano “trova una corrispondenza con esattamente uno di questi caratteri”. Così <code>[sxz]</code> significa “<code>s</code>, oppure <code>x</code>, oppure <code>z</code>”, ma solo uno di loro. Il simbolo <code>$</code> dovrebbe risultarvi familiare: corrisponde alla fine della stringa. Combinata, questa espressione regolare verifica che <var>noun</var> finisca con <code>s</code>, <code>x</code>, oppure <code>z</code>.
<li>Questa funzione <code>re.sub()</code> esegue sostituzioni di stringhe sulla base di un’espressione regolare.
</ol>
<p>Diamo un’occhiata più dettagliata alle sostituzioni di espressioni regolari.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import re</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.search('[abc]', 'Mark')</kbd> <span class=u>①</span></a>
<_sre.SRE_Match object at 0x001C1FA8>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub('[abc]', 'o', 'Mark')</kbd> <span class=u>②</span></a>
<samp class=pp>'Mork'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub('[abc]', 'o', 'rock')</kbd> <span class=u>③</span></a>
<samp class=pp>'rook'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub('[abc]', 'o', 'caps')</kbd> <span class=u>④</span></a>
<samp class=pp>'oops'</samp></pre>
<ol>
<li>La stringa <code>Mark</code> contiene <code>a</code>, <code>b</code>, oppure <code>c</code>? Sì, contiene <code>a</code>.
<li>OK, adesso trova <code>a</code>, <code>b</code>, oppure <code>c</code> e sostituiscilo con <code>o</code>. <code>Mark</code> diventa <code>Mork</code>.
<li>La stessa funzione trasforma <code>rock</code> in <code>rook</code>.
<li>Potreste pensare che questo trasformi <code>caps</code> in <code>oaps</code>, ma non lo fa. <code>re.sub()</code> sostituisce <em>tutte</em> le corrispondenze, non solo la prima. Quindi questa espressione regolare trasforma <code>caps</code> in <code>oops</code>, perché sia la <code>c</code> che la <code>a</code> vengono trasformate in <code>o</code>.
</ol>
<p>E ora, torniamo alla funzione <code>plural()</code>…
<pre class=pp><code>def plural(noun):
if re.search('[sxz]$', noun):
<a> return re.sub('$', 'es', noun) <span class=u>①</span></a>
<a> elif re.search('[^aeioudgkprt]h$', noun): <span class=u>②</span></a>
return re.sub('$', 'es', noun)
<a> elif re.search('[^aeiou]y$', noun): <span class=u>③</span></a>
return re.sub('y$', 'ies', noun)
else:
return noun + 's'</code></pre>
<ol>
<li>Qui state sostituendo la fine della stringa (a cui corrisponde il simbolo <code>$</code>) con la stringa <code>es</code>. In altre parole, state aggiungendo <code>es</code> alla stringa. Potreste ottenere la stessa cosa con la concatenazione di stringhe, per esempio <code>noun + 'es'</code>, ma ho scelto di usare le espressioni regolari in tutte le regole per ragioni che diverranno chiare più avanti in questo capitolo.
<li>Guardate attentamente, questa è un’altra nuova variazione. Il simbolo <code>^</code> come primo carattere tra parentesi quadre significa qualcosa di speciale: negazione. <code>[^abc]</code> significa “ogni singolo carattere <em>tranne</em> <code>a</code>, <code>b</code>, oppure <code>c</code>”. Allo stesso modo, <code>[^aeioudgkprt]</code> significa qualsiasi carattere tranne <code>a</code>, <code>e</code>, <code>i</code>, <code>o</code>, <code>u</code>, <code>d</code>, <code>g</code>, <code>k</code>, <code>p</code>, <code>r</code>, oppure <code>t</code>. Successivamente, quel carattere deve essere seguito da <code>h</code>, seguito a sua volta dalla fine della stringa. State cercando parole che finiscono in H dove la H può essere udita.
<li>Stesso schema qui: l’espressione regolare corrisponde a parole che finiscono in Y, dove il carattere che precede la Y <em>non</em> è <code>a</code>, <code>e</code>, <code>i</code>, <code>o</code>, oppure <code>u</code>. State cercando parole che finiscono con una Y che ha un suono /i/.
</ol>
<p>Diamo una occhiata più dettagliata alla negazione nelle espressioni regolari.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import re</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.search('[^aeiou]y$', 'vacancy')</kbd> <span class=u>①</span></a>
<_sre.SRE_Match object at 0x001C1FA8>
<a><samp class=p>>>> </samp><kbd class=pp>re.search('[^aeiou]y$', 'boy')</kbd> <span class=u>②</span></a>
<samp class=p>>>> </samp>
<samp class=p>>>> </samp><kbd class=pp>re.search('[^aeiou]y$', 'day')</kbd>
<samp class=p>>>> </samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search('[^aeiou]y$', 'pita')</kbd> <span class=u>③</span></a>
<samp class=p>>>> </samp></pre>
<ol>
<li><code>vacancy</code> corrisponde a questa espressione regolare perché finisce con <code>cy</code> e <code>c</code> non è <code>a</code>, <code>e</code>, <code>i</code>, <code>o</code>, oppure <code>u</code>.
<li><code>boy</code> non corrisponde perché finisce con <code>oy</code> e avete specificato che il carattere prima della <code>y</code> non può essere <code>o</code>. <code>day</code> non corrisponde perche termina in <code>ay</code>.
<li><code>pita</code> non corrisponde perché non termina in <code>y</code>.
</ol>
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub('y$', 'ies', 'vacancy')</kbd> <span class=u>①</span></a>
<samp class=pp>'vacancies'</samp>
<samp class=p>>>> </samp><kbd class=pp>re.sub('y$', 'ies', 'agency')</kbd>
<samp class=pp>'agencies'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub('([^aeiou])y$', r'\1ies', 'vacancy')</kbd> <span class=u>②</span></a>
<samp class=pp>'vacancies'</samp></pre>
<ol>
<li>Questa espressione regolare trasforma <code>vacancy</code> in <code>vacancies</code> e <code>agency</code> in <code>agencies</code>, che è quello che volevate. Notate che trasformerebbe anche <code>boy</code> in <code>boies</code>, ma questo non succederà mai nella funzione <code>plural()</code> perché avete utilizzato <code>re.search()</code> per capire se è il caso di applicare <code>re.sub()</code> o meno.
<li>Giusto di passaggio, voglio sottolineare che è possibile combinare queste due espressioni regolari (una per scoprire se la regola è applicabile, l’altra per applicarla effettivamente) in una singola espressione regolare. Questa riga mostra come apparirebbe l’espressione combinata. La maggior parte dell’espressione dovrebbe sembrarvi familiare: state usando un gruppo di memorizzazione, che avete imparato nella sezione <a href=espressioni-regolari.html#phonenumbers>Caso di studio: riconoscere i numeri di telefono</a>. Qui il gruppo viene usato per memorizzare il carattere prima della lettera <code>y</code>. Poi, nella stringa di sostituzione usate una nuova sintassi, <code>\1</code>, che significa “ehi, hai presente quel primo gruppo che hai memorizzato? Mettilo proprio qui.” In questo caso, avete memorizzato la <code>c</code> prima della <code>y</code>, perciò quando effettuate la sostituzione mettete <code>c</code> al posto di <code>c</code> e <code>ies</code> al posto di <code>y</code>. (Se avete memorizzato più di un gruppo, potete usare <code>\2</code> e <code>\3</code> e così via.)
</ol>
<p>Le sostituzioni di espressioni regolari sono estremamente potenti, e la sintassi <code>\1</code> le rende ancora più potenti. Ma combinare entrambe le operazioni in un’unica espressione regolare è anche più difficile da leggere, e non corrisponde direttamente al modo in cui avete precedentemente descritto le regole per la pluralizzazione. Avete originariamente esposto le regole come “se la parola finisce con S, X, o Z, allora aggiungete ES”. Se guardate la funzione <code>plural()</code>, ci sono due righe di codice che dicono esattamente “se la parola finisce con S, X, o Z, allora aggiungete ES”. Non potete essere più diretti di così.
<p class=a>⁂
<h2 id=a-list-of-functions>Una lista di funzioni</h2>
<p>Ora state per aggiungere un livello di astrazione. Avete cominciato definendo una lista di regole: se accade questo fai quello, altrimenti vai alla prossima regola. Complichiamo temporaneamente parte del programma in modo da semplificarne un’altra parte.
<p class=d>[<a href=esempi/plural2.py>scarica <code>plural2.py</code></a>]
<pre class=pp><code>import re
def match_sxz(noun):
return re.search('[sxz]$', noun)
def apply_sxz(noun):
return re.sub('$', 'es', noun)
def match_h(noun):
return re.search('[^aeioudgkprt]h$', noun)
def apply_h(noun):
return re.sub('$', 'es', noun)
<a>def match_y(noun): <span class=u>①</span></a>
return re.search('[^aeiou]y$', noun)
<a>def apply_y(noun): <span class=u>②</span></a>
return re.sub('y$', 'ies', noun)
def match_default(noun):
return True
def apply_default(noun):
return noun + 's'
<a>rules = ((match_sxz, apply_sxz), <span class=u>③</span></a>
(match_h, apply_h),
(match_y, apply_y),
(match_default, apply_default)
)
def plural(noun):
<a> for matches_rule, apply_rule in rules: <span class=u>④</span></a>
if matches_rule(noun):
return apply_rule(noun)</code></pre>
<ol>
<li>Ora, ogni regola di ricerca è contenuta in una propria funzione che restituisce i risultati della chiamata alla funzione <code>re.search()</code>.
<li>Anche ogni regola di sostituzione è contenuta in una propria funzione che chiama la funzione <code>re.sub()</code> per applicare la regola di pluralizzazione appropriata.
<li>Invece di avere una funzione (<code>plural()</code>) che contiene più regole, avete la struttura dati <var>rules</var> che è una sequenza di coppie di funzioni.
<li>Dato che le regole sono state estratte in una struttura dati separata, la nuova funzione <code>plural()</code> può essere ridotta a poche righe di codice. Usando un ciclo <code>for</code>, potete estrarre regole di ricerca e sostituzione due alla volta (una per il primo tipo, una per il secondo) dalla struttura dati <var>rules</var>. Alla prima iterazione del ciclo <code>for</code>, <var>matches_rule</var> varrà <code>match_sxz</code> e <var>apply_rule</var> varrà <code>apply_sxz</code>. Alla seconda iterazione (supponendo che ci arriviate), a <var>matches_rule</var> verrà assegnata <code>match_h</code> e ad <var>apply_rule</var> verrà assegnata <code>apply_h</code>. La funzione garantisce di restituire qualcosa alla fine, perché l’ultima regola di ricerca (<code>match_default</code>) restituisce semplicemente <code>True</code>, indicando che la regola di sostituzione corrispondente (<code>apply_default</code>) sarà sempre applicata.
</ol>
<aside>La variabile “rules” è una sequenza di coppie di funzioni.</aside>
<p>La ragione per cui questa tecnica funziona è che <a href=il-vostro-primo-programma-python.html#everythingisanobject>ogni cosa in Python è un oggetto</a>, comprese le funzioni. La struttura dati <var>rules</var> contiene funzioni — non nomi di funzioni, ma veri e propri oggetti funzione. Quando questi oggetti vengono assegnati nel ciclo <code>for</code>, allora <var>matches_rule</var> e <var>apply_rule</var> diventano vere e proprie funzioni che potete eseguire. La prima iterazione del ciclo <code>for</code> è equivalente a invocare <code>matches_sxz(noun)</code> e, se viene restituita una corrispondenza, a invocare anche <code>apply_sxz(noun)</code>.
<p>Se questo livello di astrazione aggiuntivo vi confonde, provate a sviluppare la funzione per vedere l’equivalenza. L’intero ciclo <code>for</code> è equivalente a quanto segue:
<pre class='nd pp'><code>
def plural(noun):
if match_sxz(noun):
return apply_sxz(noun)
if match_h(noun):
return apply_h(noun)
if match_y(noun):
return apply_y(noun)
if match_default(noun):
return apply_default(noun)</code></pre>
<p>Il beneficio qui è che questa funzione <code>plural()</code> è ora semplificata. Prende una sequenza di regole definite da qualche altra parte e itera attraverso di loro in modo generico.
<ol>
<li>Ottiene una regola di ricerca.
<li>Trova una corrispondenza? Allora chiama la regola di sostituzione e restituisce il risultato.
<li>Nessuna corrispondenza? Ritorna al passo 1.
</ol>
<p>Le regole potrebbero essere definite ovunque, in ogni caso. La funzione <code>plural()</code> non se ne cura.
<p>Ora, valeva la pena aggiungere questo livello di astrazione? Be’, non ancora. Consideriamo cosa bisognerebbe fare per aggiungere una nuova regola alla funzione. Nel primo esempio, questo richiederebbe l’aggiunta di un’istruzione <code>if</code> alla funzione <code>plural()</code>. Nel secondo esempio, richiederebbe l’aggiunta di due funzioni, <code>match_foo()</code> e <code>apply_foo()</code>, e l’aggiornamento della sequenza <var>rules</var> per specificare in quale ordine le nuove funzioni di ricerca e sostituzione dovrebbero essere chiamate rispetto alle altre regole.
<p>Ma questa è solo una pietra miliare verso la prossima sezione. Proseguiamo…
<p class=a>⁂
<h2 id=a-list-of-patterns>Una lista di pattern</h2>
<p>Definire separatamente una funzione con un proprio nome per ogni regola di ricerca e sostituzione non è realmente necessario. Non le chiamate mai direttamente, ma le aggiungete alla sequenza <var>rules</var> e le chiamate attraverso di essa. In più, ogni funzione segue uno di due schemi. Tutte le funzioni di ricerca chiamano <code>re.search()</code> e tutte le funzioni di sostituzione chiamano <code>re.sub()</code>. Evidenziamo gli schemi in modo che definire nuove regole diventi più semplice.
<p class=d>[<a href=esempi/plural3.py>scarica <code>plural3.py</code></a>]
<pre class=pp><code>import re
def build_match_and_apply_functions(pattern, search, replace):
<a> def matches_rule(word): <span class=u>①</span></a>
return re.search(pattern, word)
<a> def apply_rule(word): <span class=u>②</span></a>
return re.sub(search, replace, word)
<a> return (matches_rule, apply_rule) <span class=u>③</span></a></code></pre>
<ol>
<li><code>build_match_and_apply_functions()</code> è una funzione che costruisce dinamicamente altre funzioni. Accetta in ingresso <var>pattern</var>, <var>search</var> e <var>replace</var>, poi definisce una funzione <code>matches_rule()</code> che chiama <code>re.search()</code> con il <var>pattern</var> che è stato passato alla funzione <code>build_match_and_apply_functions()</code> e con la parola <var>word</var> che è stata passata alla funzione <code>matches_rule()</code> che state costruendo. Whoa.
<li>La funzione di sostituzione viene costruita allo stesso modo. Accetta un parametro e chiama <code>re.sub()</code> utilizzando sia i parametri <var>search</var> e <var>replace</var> che sono stati passati alla funzione <code>build_match_and_apply_functions()</code> sia la parola <var>word</var> che è stata passata alla funzione <code>apply_rule()</code> stessa. Questa tecnica di usare i valori di parametri esterni all’interno di una funzione dinamica è chiamata <em>chiusura</em>. State essenzialmente definendo delle costanti nell’ambito della funzione di sostituzione che state costruendo; quest’ultima accetta un parametro (<var>word</var>), ma poi agisce su quello più altri due valori (<var>search</var> e <var>replace</var>) che sono stati impostati nel momento in cui la funzione di sostituzione è stata definita.
<li>Infine, la funzione <code>build_match_and_apply_functions()</code> restituisce una tupla di due valori: le due funzioni che avete appena creato. Le costanti definite in quelle funzioni (<var>pattern</var> nell’ambito di <code>matches_rule()</code>, <var>search</var> e <var>replace</var> nell’ambito di <code>apply_rule()</code>) mantengono il loro valore in quelle funzioni persino dopo che <code>build_match_and_apply_functions()</code> le ha restituite. Questo è davvero fantastico.
</ol>
<p>Se questa tecnica vi risulta incredibilmente confusa (e dovrebbe, è roba strana), potrebbe diventare più chiara nel momento in cui vedete come si usa.
<pre class=pp><code><a>patterns = \ <span class=u>①</span></a>
(
('[sxz]$', '$', 'es'),
('[^aeioudgkprt]h$', '$', 'es'),
('(qu|[^aeiou])y$', 'y$', 'ies'),
<a> ('$', '$', 's') <span class=u>②</span></a>
]
<a>rules = [build_match_and_apply_functions(pattern, search, replace) <span class=u>③</span></a>
for (pattern, search, replace) in patterns]</code></pre>
<ol>
<li>Le nostre “regole” di pluralizzazione sono ora definite come una tupla di tuple di <em>stringhe</em> (non di funzioni). La prima stringa in ogni gruppo è l’espressione regolare che usereste in <code>re.search()</code> per vedere se c’è una corrispondenza con questa regola. La seconda e la terza stringa di ogni gruppo sono le espressioni di ricerca e sostituzione che usereste in <code>re.sub()</code> per applicare effettivamente la regola e trasformare un sostantivo nel suo plurale.
<li>C’è un leggero cambiamento qui, nella regola di ripiego. Nell’esempio precedente, la funzione <code>match_default()</code> restituiva semplicemente <code>True</code>, indicando che, se nessuna delle regole più specifiche trovava una corrispondenza, il codice avrebbe semplicemente aggiunto una <code>s</code> alla fine della parola data. Questo esempio fa qualcosa di funzionalmente equivalente. L’ultima espressione regolare chiede se la parola ha una fine (<code>$</code> corrisponde alla fine di una stringa). Naturalmente, ogni stringa ha una fine, anche la stringa vuota, quindi questa espressione trova sempre una corrispondenza. Quindi, questa espressione regolare serve allo stesso scopo della funzione <code>match_default()</code> che restituiva sempre <code>True</code>: si assicura che, se nessuna regola più specifica trova una corrispondenza, il codice aggiunga una <code>s</code> alla fine della parola data.
<li>Questa riga è magia. Prende la sequenza di stringhe in <var>patterns</var> e la trasforma in una sequenza di funzioni. Come? “Correlando” le stringhe con il valore di ritorno della funzione <code>build_match_and_apply_functions()</code>. Cioè, prende ogni tripla di stringhe e invoca la funzione <code>build_match_and_apply_functions()</code> con quelle tre stringhe come argomenti. La funzione <code>build_match_and_apply_functions()</code> restituisce una tupla di due funzioni. Questo significa che <var>rules</var> finisce per essere funzionalmente equivalente a ciò che era nell’esempio precedente: una lista di tuple, dove ogni tupla contiene una coppia di funzioni. La prima funzione è la funzione di ricerca che chiama <code>re.search()</code> e la seconda funzione è la funzione di sostituzione che chiama <code>re.sub()</code>.
</ol>
<p>Per completare questa versione del programma ecco il punto di entrata principale, la funzione <code>plural()</code>.
<pre class=pp><code>def plural(noun):
<a> for matches_rule, apply_rule in rules: <span class=u>①</span></a>
if matches_rule(noun):
return apply_rule(noun)</code></pre>
<ol>
<li>Visto che la lista <var>rules</var> è la stessa dell’esempio precedente (davvero, lo è), non dovrebbe sorprendervi che la funzione <code>plural()</code> non sia cambiata per niente. Rimane completamente generica; prende una lista di funzioni di regole e le chiama in ordine, senza preoccuparsi di come le regole sono definite. Nell’esempio precedente, le regole erano definite come funzioni separate con un proprio nome. Ora sono costruite dinamicamente attraverso la correlazione tra una lista di stringhe e il valore di ritorno della funzione <code>build_match_and_apply_functions()</code>. Non ha importanza; la funzione <code>plural()</code> continua a lavorare nello stesso modo.
</ol>
<p class=a>⁂
<h2 id=a-file-of-patterns>Un file di pattern</h2>
<p>Avete rimosso tutto il codice duplicato e avete aggiunto abbastanza astrazioni per definire le regole di pluralizzazione in una lista di stringhe. Il passo logico successivo consiste nel prendere queste stringhe e metterle in un file separato, dove possano essere mantenute separatamente dal codice che le usa.
<p>Prima di tutto, creiamo un file di testo che contiene le regole che volete. Nessuna struttura dati elaborata, solo stringhe separate da spazi bianchi su tre colonne. Chiamiamo questo file <code>plural4-rules.txt</code>.
<p class=d>[<a href=esempi/plural4-rules.txt>scarica <code>plural4-rules.txt</code></a>]
<pre class='nd pp'><code>[sxz]$ $ es
[^aeioudgkprt]h$ $ es
[^aeiou]y$ y$ ies
$ $ s</code></pre>
<p>Ora vediamo come potete usare questo file di regole.
<p class=d>[<a href=esempi/plural4.py>scarica <code>plural4.py</code></a>]
<pre class=pp><code>import re
<a>def build_match_and_apply_functions(pattern, search, replace): <span class=u>①</span></a>
def matches_rule(word):
return re.search(pattern, word)
def apply_rule(word):
return re.sub(search, replace, word)
return (matches_rule, apply_rule)
rules = []
<a>with open('plural4-rules.txt', encoding='utf-8') as pattern_file: <span class=u>②</span></a>
<a> for line in pattern_file: <span class=u>③</span></a>
<a> pattern, search, replace = line.split(None, 3) <span class=u>④</span></a>
<a> rules.append(build_match_and_apply_functions( <span class=u>⑤</span></a>
pattern, search, replace))</code></pre>
<ol>
<li>La funzione <code>build_match_and_apply_functions()</code> non è cambiata. State ancora sfruttando le chiusure per costruire dinamicamente due funzioni che usano variabili definite nella funzione esterna.
<li>La funzione globale <code>open()</code> apre un file e restituisce un oggetto file. In questo caso, il file che stiamo aprendo contiene le stringhe dei pattern per la pluralizzazione dei sostantivi. L’istruzione <code>with</code> crea quello che viene chiamato un <i>contesto</i>: quando il blocco <code>with</code> finisce, Python chiuderà automaticamente il file, anche se è stata sollevata un’eccezione all’interno del blocco <code>with</code>. Imparerete di più sui blocchi <code>with</code> e sugli oggetti file nel capitolo <a href=file.html>File</a>.
<li>L’idioma <code>for line in <oggettofile></code> legge dati da un file aperto, una riga alla volta, e assegna il testo alla variabile <var>line</var>. Imparerete di più su come leggere i file nel capitolo <a href=file.html>File</a>.
<li>Ogni riga nel file in realtà contiene tre valori, separati da spazi bianchi (che siano tabulazioni o spazi non fa alcuna differenza). Per recuperarli, usate il metodo <code>split()</code> delle stringhe. Il primo argomento del metodo <code>split()</code> è <code>None</code>, che significa “spezza la stringa in corrispondenza di qualsiasi spazio bianco (che siano tabulazioni o spazi non fa alcuna differenza)”. Il secondo argomento è <code>3</code>, che significa “spezza la stringa in corrispondenza di spazi bianchi per 3 volte, poi lascia il resto così com’è”. Una riga come <code>[sxz]$ $ es</code> verrà suddivisa nella lista <code>['[sxz]$', '$', 'es']</code>, il che significa che <var>pattern</var> avrà il valore <code>'[sxz]$'</code>, <var>search</var> avrà il valore <code>'$'</code> e <var>replace</var> avrà il valore <code>'es'</code>. Questa piccola riga di codice è molto potente.
<li>Infine, passate <code>pattern</code>, <code>search</code> e <code>replace</code> alla funzione <code>build_match_and_apply_functions()</code>, che restituisce una tupla di funzioni. Aggiungete questa tupla in coda alla lista <var>rules</var> e <var>rules</var> finirà per memorizzare la lista delle funzioni di ricerca e sostituzione che la funzione <code>plural()</code> si aspetta.
</ol>
<p>In questo caso il miglioramento è che avete completamente isolato le regole di pluralizzazione in un file esterno, in modo che possa essere gestito separatamente dal codice che lo utilizza. Il codice è codice, i dati sono dati, e la vita è bella.
<p class=a>⁂
<h2 id=generators>Generatori</h2>
<p>Non sarebbe magnifico avere una funzione <code>plural()</code> generica che analizza il file di regole? Ottieni le regole, controlla se c’è una corrispondenza, applica la trasformazione appropriata, vai alla regola successiva. Questo è tutto quello che la funzione <code>plural()</code> ha bisogno di fare, e questo è tutto quello che la funzione <code>plural()</code> dovrebbe fare.
<p class=d>[<a href=esempi/plural5.py>scarica <code>plural5.py</code></a>]
<pre class='nd pp'><code>def rules(rules_filename):
with open(rules_filename, encoding='utf-8') as pattern_file:
for line in pattern_file:
pattern, search, replace = line.split(None, 3)
yield build_match_and_apply_functions(pattern, search, replace)
def plural(noun, rules_filename='plural5-rules.txt'):
for matches_rule, apply_rule in rules(rules_filename):
if matches_rule(noun):
return apply_rule(noun)
raise ValueError('no matching rule for {0}'.format(noun))</code></pre>
<p>Come cavolo funziona <em>questo</em>? Diamo prima un’occhiata a un esempo nella shell interattiva.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>def make_counter(x):</kbd>
<samp class=p>... </samp><kbd class=pp> print('entro in make_counter')</kbd>
<samp class=p>... </samp><kbd class=pp> while True:</kbd>
<a><samp class=p>... </samp><kbd class=pp> yield x</kbd> <span class=u>①</span></a>
<samp class=p>... </samp><kbd class=pp> print('incremento x')</kbd>
<samp class=p>... </samp><kbd class=pp> x = x + 1</kbd>
<samp class=p>... </samp>
<a><samp class=p>>>> </samp><kbd class=pp>counter = make_counter(2)</kbd> <span class=u>②</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>counter</kbd> <span class=u>③</span></a>
<generator object at 0x001C9C10>
<a><samp class=p>>>> </samp><kbd class=pp>next(counter)</kbd> <span class=u>④</span></a>
<samp>entro in make_counter
2</samp>
<a><samp class=p>>>> </samp><kbd class=pp>next(counter)</kbd> <span class=u>⑤</span></a>
<samp>incremento x
3</samp>
<a><samp class=p>>>> </samp><kbd class=pp>next(counter)</kbd> <span class=u>⑥</span></a>
<samp>incremento x
4</samp></pre>
<ol>
<li>La presenza della parola chiave <code>yield</code> in <code>make_counter()</code> significa che questa non è una normale funzione. È un tipo speciale di funzione che genera valori uno alla volta. Potete pensarla come una funzione riavviabile. L’atto di chiamarla vi restituirà un <i>generatore</i> che può essere usato per generare valori successivi di <var>x</var>.
<li>Per creare un’istanza del generatore <code>make_counter()</code>, vi basta invocarlo come ogni altra funzione. Notate che la chiamata non esegue effettivamente il codice della funzione. Potete rendervene conto perché la prima riga della funzione <code>make_counter()</code> invoca <code>print()</code>, ma nulla è stato ancora stampato.
<li>La funzione <code>make_counter()</code> restituisce un oggetto generatore.
<li>La funzione <code>next()</code> accetta un generatore e restituisce il suo valore successivo. La prima volta che chiamate <code>next()</code> con il generatore <var>counter</var>, il codice in <code>make_counter()</code> viene eseguito fino alla prima istruzione <code>yield</code>, poi restituisce il valore che è stato generato. In questo caso, quel valore sarà <code>2</code>, perché avete originariamente creato il generatore invocando <code>make_counter(2)</code>.
<li>Invocare ripetutamente <code>next()</code> con lo stesso oggetto generatore fa riprendere l’esecuzione esattamente da dove era rimasta sospesa, proseguendo fino ad arrivare all’istruzione <code>yield</code> successiva. Tutte le variabili, lo stato locale, <i class=baa>&</i>c. sono salvate da <code>yield</code> e ripristinate da <code>next()</code>. La successiva riga di codice che attende di essere eseguita chiama <code>print()</code>, che stampa <samp>incremento x</samp>. Dopodiché, viene eseguita l’istruzione <code>x = x + 1</code>. Poi si passa di nuovo attraverso il ciclo <code>while</code> e la prima cosa che si esegue è l’istruzione <code>yield x</code>, che salva lo stato di tutto e restituisce il valore corrente di <var>x</var> (ora <code>3</code>).
<li>La seconda volta che chiamate <code>next(counter)</code> fate di nuovo tutte le stesse cose, ma questa volta <var>x</var> vale <code>4</code>
</ol>
<p>Dato che <code>make_counter()</code> contiene un ciclo infinito, potreste teoricamente andare avanti in questo modo per sempre, e il generatore continuerebbe semplicemente a incrementare <var>x</var> e a produrre valori in uscita. Ma diamo invece un’occhiata a usi più produttivi dei generatori.
<h3 id=a-fibonacci-generator>Un generatore di Fibonacci</h3>
<aside>“yield” sospende una funzione. “next()” la riavvia da dove era rimasta sospesa.</aside>
<p class=d>[<a href=esempi/fibonacci.py>scarica <code>fibonacci.py</code></a>]
<pre class=pp><code>def fib(max):
<a> a, b = 0, 1 <span class=u>①</span></a>
while a < max:
<a> yield a <span class=u>②</span></a>
<a> a, b = b, a + b <span class=u>③</span></a></code></pre>
<ol>
<li>La sequenza di Fibonacci è una sequenza numerica in cui ogni numero è la somma dei due numeri che lo precedono. Comincia con 0 e <code>1</code>, si incrementa lentamente all’inizio, poi sempre più rapidamente. Per cominciare la sequenza avete bisogno di due variabili: <var>a</var> parte da 0 e <var>b</var> parte da <code>1</code>.
<li><var>a</var> è il numero corrente nella sequenza, quindi producetelo in uscita.
<li><var>b</var> è il numero successivo nella sequenza, quindi assegnatelo ad <var>a</var>, ma calcolate anche il prossimo valore (<code>a + b</code>) e assegnatelo a <var>b</var> per usarlo più tardi. Notate che questo accade in parallelo: se <var>a</var> è <code>3</code> e <var>b</var> è <code>5</code>, allora <code>a, b = b, a + b</code> imposterà <var>a</var> a <code>5</code> (il valore precedente di <var>b</var>) e <var>b</var> a <code>8</code> (la somma dei valori precedenti di <var>a</var> e <var>b</var>).
</ol>
<p>Quindi avete una funzione che produce in uscita numeri di Fibonacci successivi. Certo, potreste farlo con la ricorsione, ma in questo modo è più facile da leggere. In più, funziona bene con i cicli <code>for</code>.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>from fibonacci import fib</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>for n in fib(1000):</kbd> <span class=u>①</span></a>
<a><samp class=p>... </samp><kbd class=pp> print(n, end=' ')</kbd> <span class=u>②</span></a>
<samp class=pp>0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987</samp>
<a><samp class=p>>>> </samp><kbd class=pp>list(fib(1000))</kbd> <span class=u>③</span></a>
<samp class=pp>[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]</samp></pre>
<ol>
<li>Potete usare un generatore come <code>fib()</code> direttamente in un ciclo <code>for</code>. Il ciclo <code>for</code> chiamerà automaticamente la funzione <code>next()</code> per ottenere valori dal generatore <code>fib()</code> e assegnarli alla variabile di indice del ciclo <code>for</code> (cioè <var>n</var>).
<li>A ogni iterazione nel ciclo <code>for</code>, <var>n</var> ottiene un nuovo valore dall’istruzione <code>yield</code> in <code>fib()</code>, e tutto quello che dovete fare è stamparlo. Una volta che <code>fib()</code> ha finito i numeri (<var>a</var> diventa più grande di <var>max</var>, che nel nostro caso è <code>1000</code>) allora il ciclo <code>for</code> si conclude normalmente.
<li>Questo è un utile idioma: passate un generatore alla funzione <code>list()</code> e la funzione itererà attraverso l’intero generatore (esattamente come il ciclo <code>for</code> nell’esempio precedente) per poi restituire una lista di tutti i valori.
</ol>
<h3 id=a-plural-rule-generator>Un generatore di regole per i sostantivi plurali</h3>
<p>Torniamo indietro a <code>plural5.py</code> e vediamo se questa versione della funzione <code>plural()</code> funziona.
<pre class=pp><code>def rules(rules_filename):
with open(rules_filename, encoding='utf-8') as pattern_file:
for line in pattern_file:
<a> pattern, search, replace = line.split(None, 3) <span class=u>①</span></a>
<a> yield build_match_and_apply_functions(pattern, search, replace) <span class=u>②</span></a>
def plural(noun, rules_filename='plural5-rules.txt'):
<a> for matches_rule, apply_rule in rules(rules_filename): <span class=u>③</span></a>
if matches_rule(noun):
return apply_rule(noun)
raise ValueError('nessuna regola per {0}'.format(noun))</code></pre>
<ol>
<li>Nessuna magia qui. Ricordate che le righe del file di regole contengono tre valori separati da spazi bianchi, quindi usate <code>line.split(None, 3)</code> per ottenere le tre “colonne” e assegnarle a tre variabili locali.
<li><em>E poi utilizzate <code>yield</code>.</em> Che cosa producete? Due funzioni, costruite dinamicamente con la vostra vecchia amica <code>build_match_and_apply_functions()</code> che è identica all’esempio precedente. In altre parole, <code>rules()</code> è un generatore che produce funzioni di ricerca e sostituzione <em>su richiesta</em>.
<li>Dato che <code>rules()</code> è un generatore, potete usarlo direttamente in un ciclo <code>for</code>. Alla prima iterazione nel ciclo <code>for</code> chiamerete la funzione <code>rules()</code>, che aprirà il file dei pattern, leggerà la prima riga, costruirà dinamicamente una funzione di ricerca e una funzione di sostituzione usando i pattern di quella riga e produrrà in uscita le funzioni costruite dinamicamente. Alla seconda iterazione nel ciclo <code>for</code>, <code>rules()</code> riprenderà l’esecuzione esattamente da dove era rimasta (cioè nel mezzo del ciclo <code>for line in pattern_file</code>). Come prima cosa leggerà la riga successiva del file (che è ancora aperto), poi costruirà dinamicamente un’altra coppia di funzioni di ricerca e sostituzione basate sui pattern di quella riga, infine produrrà in uscita le due funzioni.
</ol>
<p>Che cosa avete guadagnato rispetto alla versione 4? Tempo di inizializzazione. Nella versione 4, quando importavate il modulo <code>plural4</code>, il file dei pattern veniva interamente letto e una lista di tutte le possibili regole veniva costruita prima ancora che poteste anche solo pensare di chiamare la funzione <code>plural()</code>. Con i generatori, potete fare ogni cosa in maniera ritardata: leggete la prima regola, create le corrispettive funzioni e le provate, e se la prima regola è quella giusta non dovete nemmeno leggere il resto del file o creare altre funzioni.
<p>Che cosa avete perso? Prestazioni! Ogni volta che chiamate la funzione <code>plural()</code>, il generatore <code>rules()</code> ricomincia dall’inizio — il che vuol dire riaprire il file dei pattern e leggerlo dall’inizio, una riga alla volta.
<p>E se poteste avere il meglio dei due mondi? Minimo costo di inizializzazione (evitando di eseguire codice alla chiamata di <code>import</code>) <em>e</em> massime prestazioni (evitando di costruire sempre le stesse funzioni ogni volta). Oh, e volete ancora mantenere le regole in un file separato (perché il codice è codice e i dati sono dati), fino a quando non dobbiate leggere la stessa riga due volte.
<p>Per farlo, avrete bisogno di costruire un vostro iteratore. Ma prima che possiate fare <em>questo</em>, dovete imparare le classi Python.
<p class=a>⁂
<h2 id=furtherreading>Letture di approfondimento</h2>
<ul>
<li><a href=http://www.python.org/dev/peps/pep-0255/><abbr>PEP</abbr> 255: Generatori semplici</a>
<li><a href=http://effbot.org/zone/python-with-statement.htm>Capire l’istruzione “with” di Python</a>
<li><a href=http://ynniv.com/blog/2007/08/closures-in-python.html>Le chiusure in Python</a>
<li><a href=http://en.wikipedia.org/wiki/Fibonacci_number>I numeri di Fibonacci</a>
<li><a href=http://www2.gsu.edu/~wwwesl/egw/crump.htm>I sostantivi plurali irregolari in inglese</a>
</ul>
<p class=v><a rel=prev href=espressioni-regolari.html title='indietro a “Espressioni regolari”'><span class=u>☜</span></a> <a rel=next href=iteratori.html title='avanti a “Classi & iteratori”'><span class=u>☞</span></a>
<p class=c>© 2001–10 <a href=informazioni-sul-libro.html>Mark Pilgrim</a><br>
© 2009–10 <a href=informazioni-sulla-traduzione.html>Giulio Piancastelli</a> per la traduzione italiana
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>