forked from mar-leone/Logica-2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
02_programmi_ricorsive.tex
1483 lines (1283 loc) · 61.7 KB
/
02_programmi_ricorsive.tex
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
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\chapter{Funzioni Ricorsive}
\label{lemma diagonalizzazione}
Lo scopo di questo capitolo \`e quello di dimostrare l'equivalenza tra
i concetti di calcolabilit\`a con registri e calcolabilit\`a con
programmi. Iniziamo dunque a definire i programmi.
\section{I programmi}
Nella sezione precedente abbiamo visto come il contenuto dei registri,
nelle macchine a registri, possa essere modificato attraverso
determinate \emph{istruzioni} che la macchina \`e in grado di
riconoscere. Una sequenza di queste semplici istruzioni costituisce un
\emph{programma}.
\begin{programmi}
Chiamiamo programma P una sequenza finita non nulla di istruzioni etichettate
\end{programmi}
Chiameremo $x_i$, $x_j$ e $x_k$ variabili dove $i,j,k \in \mathbb{N}$. Assumeremo come convenzione,
che l'output di $P$ deve essere messo (dal programmatore) nella variabile $x_0$.
Inoltre l'ennupla $a_1, a_2, ..., a_n \in \mathbb{N}^k $ fornita come input al nostro programma verrà memorizzato nelle prime n variabili $x_1, ..., x_n$.
Assumiamo anche che tutte le variabili utilizzare nel nostro programma siano automaticamente inizializzate a zero e scriveremo $P(a_1, a_2, ..., a_n)$ per dire che eseguiamo $P$ con l'input $a_1, a_2, ..., a_n$.
Le istruzioni del programma possono essere:
\begin{enumerate}
\item $x_j \leftarrow 0$ assegna ad $x_j$ il valore zero. (azzeramento)
\item $x_j \leftarrow x_i $ assegna il contenuto di $x_i$ a $x_j$ (trasferimento)
\item $x_j \leftarrow x_j+1$ aumenta il contenuto di $x_j$ di uno e
riassegna il risultato come valore a $x_j$ (successsore)
\item $\textrm{if }(condizione) $ then goto $\alpha$ else goto $\beta$ con
$\alpha$ e $\beta$ etichette di istruzioni del programma. (salto)
\end{enumerate}
Esempi di condizioni possono essere il confronto fra due variabili, il confronto di
una variabile con un numero naturale ecc..
\begin{nota}
Nella (\emph{4}) come condizione \`e possibile utilizzare
una qualunque formula atomica di un linguaggio che usi le relazioni
$=,\geq,<$ e le operazioni $+,-,successore,\ldots $ e le loro
composizioni.
\end{nota}
\begin{extra}
Si scriva un programma in cui sia presente un'istruzione del tipo
(\emph{4}) avente per condizione $x_i\neq x_j$. (\emph{Suggerimento}:
si pensi ad una funzione in $(x_i,\ldots ,x_j)$ il cui valore sia
diverso da $0$ quando $x_i\neq x_j$)
\end{extra}
\begin{extra}
Mostrare, con qualche esempio, che \`e possibile sostituire alla
condizione della (\emph{4}) una qualsiasi espressione di
calcolo \newline booleano classico.
\end{extra}
Per eseguire una \emph{computazione} la macchina deve essere
fornita di un \emph{programma} $P$ e di una \emph{configurazione
iniziale} (input), che pu\`o essere, ad esempio, una sequenza $a_1, a_2,
...$ di numeri naturali.
Supponiamo che $P$ sia composto da $s$ istruzioni $I_1, I_2,...,
I_s$.
Allora la macchina inizia la computazione eseguendo $I_1$, poi $I_2$,
e cos\`i via, a meno che non si incontri un'istruzione di \emph{goto}
che fa saltare la computazione in una delle $s$ istruzioni. La
computazione si ferma quando, e solo quando non c'\`e una prossima
istruzione oppure incontriamo la parola chiave \textbf{stop}.
Possiamo illustrare questo attraverso un esempio.
\begin{esempio} Consideriamo il seguente programma con $s=6$ istruzioni:
\begin{mylisting}
$I_1$: $\textrm{if }x_1 > x_2$ then goto $I_2$ else goto $I_5$
\\ $I_2$: $x_2 \leftarrow x_2+1$\\ $I_3$: $x_3 \leftarrow
x_3+1$\\ $I_4$: $\textrm{if }x_1 \neq x_2$ then goto $I_2$ else goto
$I_5$\\ $I_5$: $x_0 \leftarrow x_3$\\
\end{mylisting}
E con la seguente configurazione iniziale:
\begin{mylisting}
$x_0 = 0, x_1 = 9, x_2 = 7, x_3 = 0, ...$
\end{mylisting}
Durante il calcolo le variabili vengono modificate come segue:
\begin{mylisting}
$x_0 = 0, x_1 = 9, x_2 = 7, x_3 = 0, ...$ ($I_1: x_1 > x_2$)\\ $x_0 =
0, x_1 = 9, x_2 = 8, x_3 = 0, ...$ ($I_2$)\\ $x_0 = 0, x_1 = 9, x_2
= 8, x_3 = 1, ...$ ($I_3$)\\ $x_0 = 0, x_1 = 9, x_2 = 8, x_3 = 1,
...$ ($I_4: x_1 \neq x_2$)\\ $x_0 = 0, x_1 = 9, x_2 = 9, x_3 = 1,
...$ ($I_2$)\\ $x_0 = 0, x_1 = 9, x_2 = 9, x_3 = 2, ...$
($I_3$)\\ $x_0 = 0, x_1 = 9, x_2 = 9, x_3 = 2, ...$ ($I_4: x_1 =
x_2$)\\ $x_0 = 2, x_1 = 9, x_2 = 9, x_3 = 2, ...$ ($I_5$)\\
\end{mylisting}
\end{esempio}
\begin{nota}
Al momento non poniamo la nostra concentrazione su quale funzione
calcola questo programma, ma ci limitiamo ad illustrare in quale modo
opera un programma in senso puramente meccanico.
\end{nota}
\begin{extra}
Dare un programma che memorizza in $x_0$ 1 se $x_1 > x_2$ e 0
altrimenti.
\end{extra}
Per comprendere il significato del programma e l'andamento della
computazione \`e spesso conveniente desciverlo in modo informale
attraveso un \emph{Flow Chart}, per esempio il flow chart
rappresentante il programma dell'Esempio 1.1 \`e dato in Figura
1. Notare che i test contenuti nei rombi rappresentano le istruzioni
\emph{if then else} (4) che hanno quindi due prosecuzioni in base al
risultato del test; mentre i rettangoli sono le istruzioni di
successore o assegnazione che continuano sempre con la prossima
istruzione.
\begin{figure}[h]
\begin{tikzpicture}[node distance = 1.6 cm, auto]
% Place nodes
\node[noblock] (start) {START};
\node [decision, below of=start] (I1) {$x_1 > x_2$};
\node [block, below of=I1] (I2) {$x_2 \leftarrow x_2 + 1$};
\node [block, below of=I2] (I3) {$x_3 \leftarrow x_3 + 1$};
\node [decision, below of=I3] (I4) {$x_1 \neq x_2$};
\node [block, below of=I4] (I5) {$x_0 \leftarrow x_3$};
\node[noblock,below of=I5] (stop) {STOP};
% Draw edges
\path [line] (start) -- (I1); \path [line] (I1) --
node{yes}(I2); \path [line] (I1.east) -- ++(1.0,0) -- ++(0,-1) |-
node [near start] {no}(I5.east); \path [line] (I2) -- (I3); \path
[line] (I3) -- (I4); \path [line] (I4.west) -- ++(-.8,0) --
++(-.8,0) |- node [near start] {yes}(I2.west); \path [line] (I4)
--node{no} (I5); \path [line] (I5) -- (stop);
\end{tikzpicture}
\caption{Flow Chart}
\end{figure}
Dall'esempio 1 notiamo inoltre come il comando \emph{goto} nella
definizione dell'\emph{if} renda possibile l'esecuzione di cicli,
infatti l'istruzione $I_4$ riporta l'esecuzione all'istruzione $I_2$
tante volte fino a quando la condizione $x_1 \neq x_2$ non viene
soddisfatta.
Pi\`u in generale possiamo dire che il comando \emph{goto} pu\`o
essere utilizzato per simulare quello che nei linguaggi ad alto livello conosciamo come
ciclo \emph{for}.
Infatti con $n$ fissato il ciclo:\\
$$ \left[
\begin{array}{l}
for \; $i = 1$ \; to \; $n$\\ \ \ \ \ \left[P\;
programma]\right. \\ next \; $i$\\
\end{array} \right.
\
$$
viene implementato dal programma nell'Esempio 1.2 dove $n$ è un opportuna variabile
che riceve in input il valore di $n$.
\newpage
\begin{esempio}
\begin{minipage}[c]{.50\textwidth}
\begin{mylisting}
$I_1: x_i \leftarrow 0 $\\
$I_2: x_i \leftarrow x_i + 1 $ \\
$I_3: $ if $ n<x_i $ then goto stop else goto $I_4$ \\
$I_4: \left[ P\; programma \right ] $ \\
$I_5: x_i \leftarrow x_i+1 $ \\
$I_6 $ if $n < x_i $ then \\ \hspace{\stretch{1}} goto stop else goto $I_4$\\
\end{mylisting}
\end{minipage}
\hspace{5mm}%
\begin{minipage}[c]{.50\textwidth}
\begin{tikzpicture}[node distance = 1.6 cm, auto]
% Place nodes
\node[noblock] (start) {START};
\node [block, below of=start] (I1) {$x_i \leftarrow 0$};
\node [block, below of=I1] (I2) {$x_i \leftarrow x_i+ 1$};
\node [decision, below of=I2] (I3) {$n < x_i$};
\node [block, below of=I3] (I4) {$ \left [ P \right] $};
\node [block, below of=I4] (I5) {$x_i \leftarrow x_i+ 1$};
\node [decision, below of=I5] (I6) {$n < x_i$};
\node[noblock,below of=I6] (stop) {STOP};
% Draw edges
\path [line] (start) -- (I1);
\path [line] (I1) -- (I2);
\path [line] (I2) -- (I3);
\path [line] (I3.west) -- ++(-.8,0) -- ++(-.8,0) |- node [near start] {yes}(stop.west);
\path [line] (I3) -- node{no} (I4);
\path [line] (I4) -- (I5);
\path [line] (I5) -- (I6);
\path [line] (I6.east) -- ++(+.10,0) -- ++(+.8,0) |- node [near start] {no}(I4.east);
\path [line] (I6) --node{yes} (stop);
\end{tikzpicture}
\end{minipage}
\end{esempio}
\vspace{5mm}%
\newpage
Ci chiediamo adesso, le quattro istruzioni che abbiamo introdotto sono tutte necessarie?
E' possibile toglierne qualcuna e continuare a calcolare le stesse funzioni?
E immediato verificare che l'istruzione di trasferimento $x_j \leftarrow x_i$ può
essere scritta in funzione delle altre. Vediamo l'esempio:
\begin{esempio} utilizziamo le altre tre istruzioni per simulare il comportamento dell' istruzione di trasferimento:
\begin{mylisting}
$I_1$: $\textrm{if }x_j = x_i$ then goto stop else goto $I_2$\\
$I_2$: $x_j \leftarrow 0$\\
$I_3$: $x_j \leftarrow x_j +1$\\
$I_4$: $\textrm{if }x_j = x_i$ then goto stop else goto $I_2$\\
\end{mylisting}
\end{esempio}
Abbiamo quindi introdotto l'istruzione di trasferimento al solo fine di semplificare
la scrittura dei nostri programmi.
Un altra domanda che sorge è: "senza l'istruzione di salto
riusciamo a calcolare le stesse funzioni di quante ne calcoleremo con tutte e quattro? Ebbene si può dimostrare che
le uniche funzioni che possiamo calcolare senza l'istruzione di salto sono del tipo
\begin{enumerate}
\item $f(x) = m \, \, m \in \mathbb{N} $
\item $f(x) = x+ m \, \, \forall x , m \in \mathbb{N} $
\end{enumerate}
\begin{extra}
Dimostrare che senza l'istruzione di if .. goto calcoliamo solo funzioni del tipo (1) e (2).
\end{extra}
\begin{extra}
Dimostrare che senza l'istruzione di if .. goto calcoliamo solo funzioni che sono totali.
\end{extra}
Questo fatto ci mostra che con tutta la sua semplicità l'istruzione di salto
è importantantissima per calcolare quanto c'e di calcolabile e ciò si traduce nel fatto che
se il nostro linguaggio di programmazione preferito non disponesse dell' istruzione di salto
esso servirebbe a ben poco.
Notiamo anche il fatto che non tutti i moderni linguaggi di programmazione
dispongono dell'istruzione goto dovuto al fatto che l'uso questa istruzione
è generalmente considerato indice di cattiva programmazione
ma ciò è stato rimpiazzato dall'uso delle cosiddette strutture di controllo iterative (for e while)
che implicitamente permettono di saltare in maniera controllata da un punto
all'altro del programma.
\subsection{Funzioni calcolabili con un programma}
Abbiamo visto dei capitoli precedenti come in maniera naturale "leghiamo" il
concetto di macchina al concetto di funzione, così come in questo
capitolo abbiamo legato il concetto di programma a quello di funzione.
Cioè è naturale pensare il calcolo del valore di una qualsiasi funzione $f(a_1, a_2, ... , a_n)$
per mezzo di un programma $P$ con configurazione iniziale $a_1, a_2, ... , a_n$.
In generale, la funzione associata ad un programma è parziale, anziché totale, in quanto tra le caratteristiche dei nostri programmi, rientra la
posibilità che, per certi input non venga prodotto alcun output.
Si consideri ad esempio il seguente programma:
\begin{mylisting}
$I_1$: if $x_i = x_i $ then goto $I_1$ else goto stop
\end{mylisting}
che non termina mai. La condizione è sempre vera è ciò causa l'esecuzione
continua della stessa istruzione. La funzione calcolata
da questo programma la chiameremo funzione sempre indefinita.
Diamo adesso una definizione di funzione calcolabile con un programma.
\begin{programmi}
Una funzione $f$ si dice \emph{calcolabile da un programma} se,
$\exists$ un programma $P$ tale che per ogni $(a_1,...,a_n) \in \mathbb{N}^k$
$P(a_1,...,a_n)$ termina sse $(a_1,...,a_n) \in dom(f) $ e $x_0=a$ se
$f(a_1,...,a_n) = a$
\end{programmi}
\subsection{Equivalenza tra programmi e macchine a registri}
E' facile verificare che con una macchina a registri si possono
eseguire le stesse operazioni viste all'inizio della sezione (e
viceversa), e in effetti vale il seguente teorema.
\begin{progabaco}
Ogni funzione \'e computabile con un programma se e solo se \'e
computabile con una macchina a registri.
\end{progabaco}
\textsc{Dimostrazione} $(\Rightarrow)$ Vediamo come le macchine a
registri sono in grado di calcolare le quattro istruzioni che
costituiscono un programma:\\
\begin{enumerate}
\item
$x_j \leftarrow 0$ \hspace{10mm} \xymatrix{ \roundentry{j-}
\ar@(ul,ul)[] \ar@(ul,dl)[] \ar[r] & stop }
\vspace{10mm}
\item $x_j \leftarrow x_i $ \hspace{10mm} \xymatrix{ \roundentry{j-}
\ar@(ul,ul)[] \ar@(ul,dl)[] \ar[r] & \roundentry{i-} \ar[d] \ar[r] &
\roundentry{42-} \ar@(dr,d)[d] \ar[r] & stop \\ & \roundentry{j+} \ar[d] & \roundentry{i+} \ar[u]
\\ & \roundentry{42+} \ar@(dl,dl)[uu]}
\vspace{10mm}
\item $x_j \leftarrow x_j+1$ \hspace{10mm} \xymatrix{ \roundentry{j+}
\ar@(ul,ul)[] \ar[d] \\ ... }
\vspace{10mm}
\item $\textrm{if }x_i \neq0$ then goto $\alpha$ else goto
$\beta$ \hspace{10mm} \xymatrix{ \roundentry{i-} \ar@(ul,ul)[]
\ar[d] \ar[r] & \beta\\ \roundentry{i+} \ar[d] \\ \alpha }
\end{enumerate}
$(\Leftarrow)$ Si tratta di simulare con (1)$\rightarrow$(4) le
operazioni di una macchina a registri:
\begin{enumerate}
\item esegue l'incremento di uno, la prima operazione elementare di
una macchina a registri;\\ \\
\hspace{10mm} \xymatrix{\roundentry{j+} \ar@(ul,ul)[] \ar[d] \\ ...}
\hspace{10mm} $x_j \leftarrow x_j+1$
\item la seconda operazione elementare sulle macchine a registri si
ottiene usando (4) e ponendo in $\alpha$ un'istruzione che sottragga
1 al registro se non \'e vuoto, altrimenti il programma esegue
l'istruzione $else$ $\beta$, che corrisponde all'operazione di
procedere a destra.\\ \\ \xymatrix{ \roundentry{i-} \ar@(ul,ul)[]
\ar[d] \ar[r] & \beta\\ \alpha }
\hspace{10mm} $\textrm{if }x_i \neq 0$ then goto $\alpha$ else goto
$\beta$ \\ con $\alpha = x \stackrel{\centerdot}{-} 1$ e $\beta=$
'procedi a destra'.
\end{enumerate}
\hspace{\stretch{1}} $\Box$\\
\begin{esempio}[Differenza]
\ Come esempio riportiamo il calcolo della funzione differenza
definita come segue:\\
\begin{center}
$n \stackrel{\centerdot}{-} m= \left\{ \begin{array}{ll} n-m &
\textrm{se } m \leq n\\ 0 & \textrm{altrimenti}
\end{array} \right.$
\end{center}
\begin{figure}[h]
\hspace{0cm} \xymatrix{ \roundentry{1-} \ar[r] \ar@(dr,d)[d] & stop
\\ \roundentry{2-} \ar@(ul,ul)[] \ar[u] \ar[r] & \roundentry{1-}
\ar[d] \ar[r] & stop \\ & \roundentry{42+} \ar@(d,dl)[u] }
\caption{Macchina a registri che calcola la differenza definita sui
naturali $n \stackrel{\centerdot}{-} m$}
\end{figure}
\ \\ Tale funzione viene calcolata dalla macchina a registri in Figura
2 e dal programma o che abbiamo esaminato nell'Esempio 1.1.
\end{esempio}
Il teorema appena enunciato stabilisce la completa equivalenza tra i
concetti di calcolabilità con registri e calcolabilità con
programmi. Si noti come attraverso queste equivalenze si giunga ad un
livello di astrazione sempre maggiore, e come proprio questa
progressione ci fornisca strumenti via via pi\'u versatili attraverso
i passaggi per la dimostrazione dei teoremi di incompletezza.
\section{Funzioni primitive ricorsive}
Vogliamo definire ora una classe che comprenda tutte e sole le
funzioni calcolabili. Seguiremo a tale scopo Post, il quale si serve
di una definizione ricorsiva. Una definizione \`e detta
\emph{ricorsiva} quando ci\`o che \`e da definirsi viene definito
facendo ricorso a istanze pi\`u elementari dello stesso problema.
Tale metodo consiste nel:
\begin{itemize}
\item fissare un insieme di funzioni iniziali immediatamente
calcolabili quale base della procedura di definizione
\item indicare alcune regole per derivare altre funzioni ricorsive da
quelle date in partenza (regole che ovviamente preservino la
calcolabilità delle funzioni derivate)
\item escludere dalla classe delle funzioni ricorsive quelle funzioni
che non siano le iniziali o da queste derivabili.
\end{itemize}
Diamo dunque la seguente definizione:
\begin{programmi}
Si dice \emph{funzione primitiva ricorsiva} un elemento della
classe definita induttivamente a partire dalle seguenti funzioni:
\begin{itemize}
\item [a.] la funzione nulla $z$ tale che $z(x)=0$;
\item [b.] la funzione proiezione $p^n_i$ tale che $p_i^n(x_1,\ldots
,x_n)=x_i$ $\forall i\leq n$
\item [c.]la funzione successore $s$ tale che $s(x) = x+1$;
\end{itemize}
\end{programmi}
Le regole per produrre nuove funzioni sono:
\begin{enumerate}
\item le operazioni di \emph{\emph{composizione}}, che date le
funzioni primitive ricorsive $f: \mathbb{N}^k \rightarrow
\mathbb{N}$ e $g_i : \mathbb{N}^n \rightarrow \mathbb{N}$ per $i= 1,
... , k$ permette di ottenere una funzione $h : \mathbb{N}^n
\rightarrow \mathbb{N}$ per cui:\\ $
h(x_1,.....,x_n)=f(g1(x_1,.....,x_n),....., g_k (x_1,.....,x_n))$
\\ anch'essa primitiva ricorsiva.
\item lo schema di \emph{\emph{ricorsione primitiva}}, che date
le funzioni primitive ricorsive $f: \mathbb{N}^k \rightarrow
\mathbb{N}$ e $g : \mathbb{N}^{k+2} \rightarrow \mathbb{N}$, allora
\`e primitiva ricorsiva anche la funzione $h : \mathbb{N}^{k+1}
\rightarrow \mathbb{N}$ che soddisfi il sistema di equazioni:
\begin{center}
$\left\{ \begin{array}{ll} h(x_1,.....,x_n,0)= f(x_1,.....,x_n)
\\ h(x_1,.....,x_n,s(y))=g(x_1,.....,x_n, y, h(x_1,.....,x_n,y))
\end{array} \right.$
\end{center}
\end{enumerate}
\vspace{5mm}%
La classe delle funzioni primitive ricorsive \`e dunque \emph{chiusa
rispetto alle operazioni di composizione e ricorsione
primitiva}. Ora vedremo che questa classe di funzioni, che abbiamo
appena definito in termini matematici, \`e calcolabile dai programmi.
\begin{thm}
\emph{Ogni funzione primitiva ricorsiva è eseguibile da un programma
(e quindi calcolabile).}\end{thm}
\begin{proof}
Dobbiamo fare un programma per le tre funzioni di base, per la
composizione generalizzata e la ricorsione. Come sappiamo l' output è
per convenzione in $x_{0}$. Assumiamo anche in questa dimostrazione che
se un programma P utilizza una variabile per la prima volta prima di usarla
azzeri il suo contenuto. Questa assunzione deve essere fatta perché
quando parleremo di ricorsione e composizione delle funzioni
i programmi verranno eseguiti uno dopo l'altro e tra le loro possibilità rientra quella
di utilizzare le stesse variabili usate dai programmi precedenti. Ciò assicura
che azzerando la variabile prima dell'uso non si trovino valori `sporchi`.
\begin{itemize}
\item[a.] Facciamo un programma per la funzione zero:
\begin{mylisting}
$I_1$: $x_{0}\leftarrow0$
\end{mylisting}
\item[b.] Il programma per il successore:\begin{mylisting}$I_1$:
$x_{1}\leftarrow x_{1}+1$ \\$I_2:x_{0}\leftarrow x_{1} $\end{mylisting}
\item[c.] La proiezione:\begin{mylisting}$I_1$: $x_{0}\longleftarrow
x_{i}$\end{mylisting}
\end{itemize}
\begin{enumerate}
\item La composizione: siano $F, G_1, G_2, \dots, G_k$ programmi che calcolano
le funzioni $f,g_{1},...g_{k}$ rispettivamente. Dobbiamo far vedere che
riusciamo ad esibire un programma $H$ che calcola la funzione $h$.
L'output di ogni
funzione viene salvato in $x_{0}$; quindi, poich\'e vogliamo salvare
tutti i risultati intermedi del calcolo di $g_{1},\dots,g_{k}$
dobbiamo copiare di volta in volta $x_0$ in una variabile che siamo
sicuri che non sia usata da uno dei programmi. Inoltre dobbiamo
riservarci dello spazio per le variabili di input poich\'e qualche
programma potrebbe modificarle durante la sua esecuzione. Per
sapere dove possiamo salvare tutti questi valori dobbiamo sapere
quali variabili vengono utilizzate dai programmi.
Sia quindi
$$\rho(Q)=max\left\{ i|x_{i}\leftarrow...\in Q\right\}$$
il massimo indice di una variabile assegnata in $Q$ e
$$\varepsilon=max\left\{
\rho(G_{1}),....,\rho(G_{k}),\rho(F),k,n\right\}$$
il massimo indice utilizzato nei programmi $F, G_1, \dots, G_k$.
Costruiamo il programma $H$ che calcola la composizione come segue:
salviamo l'input, che si trova in $x_{1},\dots,x_{n}$ in
$x_{\varepsilon+1},\dots,x_{\varepsilon+n}$, eseguiamo il programma
$G_1$ e copiamo l'output in $x_{\varepsilon+n+1}$:
\begin{mylisting}
$x_{\varepsilon+1}\leftarrow x_{1}$\\
$\vdots$\\
$x_{\varepsilon+n}\leftarrow x_{n}$\\
$[G_{1}]$\\
$x_{\varepsilon+n+1}\leftarrow x_{0}$
\end{mylisting}
Copiamo l'input:
\begin{mylisting}
$x_{1}\leftarrow x_{\varepsilon+1}$\\
$\vdots$\\
$x_{n}\leftarrow x_{\varepsilon+n}$
\end{mylisting}
Eseguiamo $G_{2}$ e copiamo l'output in $x_{\varepsilon+n+2}$;
ripetiamo questa operazione per tutti gli altri programmi fino ad
applicare il programma $G_{k}$ e copiare il suo output in
$x_{\varepsilon+n+k}$.
A questo punto tutti gli input per F si trovano in
$x_{\varepsilon+n+1},\dots,x_{\varepsilon+n+k}$. Copiamo questi
valori in $x_{1},\dots,x_{k}$ e applichiamo il programma F:
\begin{mylisting}
$x_{1}\leftarrow x_{\varepsilon+n+1}$\\
$\vdots$\\
$x_{k}\leftarrow x_{\varepsilon+n+k}$\\
$[F]$
\end{mylisting}
Dopo questa sequenza di operazioni il risultato della composizione si
trova in $x_{0}$.
\item La ricorsione: sia $F$ un programma che calcola $f$ e $G$ un
programma per $g$. Per realizzare un programma $H$ che calcola $h$
dobbiamo innanzitutto salvare le variabili di input e i risultati
parziali di $f$ e $g$ in variabili non utilizzate dai due programmi
$F$ e $G$.
Sia $\rho(Q)=max\left\{ i|x_{i}\leftarrow\dots \in Q\right\}$ il
massimo indice utilizzato nel programma Q; definiamo
$\varepsilon=max\left\{ \rho(F),\rho(G),k+2\right\}$ il massimo
indice utilizzato per entrambi i programmi.
Allora, prima copiamo l'input e mettiamo 0 in $x_{\varepsilon+k+2}$
per cominziare la ricorsione (per calcolare $h(x,0)$). Adesso possiamo
applicare il programma $F$.
Poi dobbiamo guardare se l'$y$ che ci è stato dato all'inizio che copiamo
in $x_{\varepsilon+k+3}$ è uguale
a zero; se è cosi, il programma deve terminare. Se non è cosi, il
programma deve calcolare $h(x,s(y))$ (la prima volta $s(y)$ sarà
1). Il programma G utilizza come input $x_{1},\dots,x_{k}$ per
$x_{1},\dots,x_{k}$, $x_{k+1}$ per $y$ e $x_{k+2}$ per il risultato
parziale $h(x,y)$. Quindi dobbiamo mettere l'input iniziale in
$x_{1},\dots,x_{k}$, il risultato al passo precedente che si trova in
$x_{\varepsilon+k+2}$ dobbiamo copiarlo in $x_{k+1}$ e, dopo aver
eseguito $G$, dobbiamo copiare il suo output da $x_{0}$ in $x_{k+2}$.
Infine calcoliamo il successore di $y$ e controlliamo se equivale
all'$y$ iniziale: in caso affermativo dobbiamo terminare, altrimenti
torniamo ad eseguire il ciclo. Allora, il programa sarà:
\begin{mylisting}
$x_{\varepsilon+1}\leftarrow x_{1}$\\
$\vdots$\\
$x_{\varepsilon+k}\leftarrow x_{k}$\\
$x_{\varepsilon+k+3}\leftarrow x_{k+1}$ \\
$[F]$\\
$x_{\varepsilon+k+2}\leftarrow x_{0}$\\
$x_{\varepsilon+k+1}\leftarrow 0$\\
$I_t:\; $if$\; x_{\varepsilon+k+3}=x_{\varepsilon+k+1}\; $then goto$\;stop$ else goto $I_{t+1}$\\
$I_{t+1}:x_{1}\leftarrow x_{\varepsilon+1}$\\
$\vdots$\\
$x_{k}\leftarrow x_{\varepsilon+k}$\\
$x_{k+1}\leftarrow x_{\varepsilon+k+1}$\\
$x_{k+2} \leftarrow x_{\varepsilon+k+2}$\\
$[G]$\\
$x_{\varepsilon+k+2} \leftarrow x_0$\\
$x_{\varepsilon+k+1}\leftarrow x_{\varepsilon+k+1}+1$\\
$if \; x_0=x_0\; $ then goto $I_t$ else ...
\end{mylisting}
\end{enumerate}
\end{proof}
% Controllare
\subsection{Esempi di funzioni primitive ricorsive}
\begin{esempio}[somma]
$h:\mathbb{N}^2 \to \mathbb{N}$, $h(x,y)=x+y$.
Possiamo usare lo schema semplificato
$\left\{ \begin{array}{ll}
x+0=x\\
x+s(y)=s(x+y)
\end{array}\right.$.\\
Nel nostro schema di ricorsione la stessa funzione si ottiene con $h$
tale:\newline
$$\begin{array}{ll}
h(x,0)=f(x)=p_1^1(x)=x\\
h(x,s(y))=g(x,y,h(x,y))=s(p_3^3(x,y,h(x,y)))=s(h(x,y)).
\end{array}$$\newline
\end{esempio}
%
\begin{esempio}[prodotto]
$h:\mathbb{N}^2 \to \mathbb{N}$, $h(x,y)=x \cdot y$ che possiamo scrivere
come
$\left\{ \begin{array}{ll}
x \cdot 0=0\\
x \cdot s(y)= x \cdot y + x
\end{array}\right.$ e quindi: \newline
$$\begin{array}{ll}
h(x,0)=f(x)=z(x)\\
h(x,s(y))=g(x,y,h(x,y))=p_3^1(x,y,h(x,y))+p_3^3(x,y,h(x,y)).
\end{array}$$\newline
\end{esempio}
%
\begin{esempio}[fattoriale] Per adattare la definizione della funzione
fattoriale
$h:\mathbb{N} \to \mathbb{N}$, $h(x)=x!$ allo schema generale si considera
$\left\{ \begin{array}{ll}
0!=1\\
s(y)! = y! \cdot s(y)
\end{array}\right.$ e quindi si pu\`o prendere $h$ come segue: \newline
$$\begin{array}{ll}
h(x,0)=f(x)=s(z(x))=1\\
h(x,s(y))=g(x,y,h(x,y))= s(p_3^2(x,y,h(x,y)))) \cdot p_3^3(x,y,h(x,y))=
\\
\hspace{1.8cm} = s(y)\cdot h(x,y).
\end{array}$$\newline
\end{esempio}
%
%
\begin{esempio}[elevamento a potenza] $h:\mathbb{N}^2 \to \mathbb{N}$,
$h(x,y)=x^y$:
$\left\{ \begin{array}{ll}
x^0=1\\
x^{s(y)} = x^y \cdot x
\end{array}\right.$ e quindi: \newline
$$\begin{array}{ll}
h(x,0)=f(x)=s(z(x))=1\\
h(x,s(y))=g(x,y,h(x,y))= p_3^1(x,y,h(x,y)) \cdot p_3^3(x,y,h(x,y)) =
x\cdot h(x,y).
\end{array}$$\newline
\end{esempio}
%
%
\begin{esempio}[predecessore] $p:\mathbb{N} \to \mathbb{N}$ tale che
$\left\{ \begin{array}{ll}
p(0)=0\\
p(s(y)) = y
\end{array}\right.$ e quindi: \newline
$$\begin{array}{ll}
p(0)=f=0\\
p(s(y))=g(y,p(y))= p_2^1(y,p(y)).
\end{array}$$\newline
\end{esempio}
%
%
\begin{esempio}[sottrazione] $h:\mathbb{N}^2 \to \mathbb{N}$,
$ h(x,y) = \left\{ \begin{array}{ll}
x \stackrel{\centerdot}{-} y \ se \ y \leq x\\
0 \ altrimenti
\end{array}\right.$ dunque: \newline
$$\begin{array}{ll}
h(x,0)=f(x)=x\\
h(x,s(y))=p(h(x,y)).
\end{array}$$\newline
\end{esempio}
%
%
\begin{esempio}[segno] $sgn:\mathbb{N} \to \left\{0,1\right\}$,
$ sgn(y) = \left\{ \begin{array}{ll}
0 \ se \ y = 0\\
1 \ se \ y > 0
\end{array}\right.$ che si pu\`o scrivere come:
\[ sgn(y) = y \stackrel{\centerdot}{-} p(y) \]
che \`e primitiva ricorsiva per quanto visto negli esempi precedenti.
\end{esempio}
%
%
\begin{esempio}[controsegno] $\overline{sgn}:\mathbb{N} \to
\left\{0,1\right\}$,
$ \overline{sgn}(y) = \left\{ \begin{array}{ll}
1 \ se \ y = 0\\
0 \ se \ y > 0
\end{array}\right.$ che si pu\`o pensare come:
\[ \overline{sgn}(y) = 1 \stackrel{\centerdot}{-} sgn(y) \]
dunque \`e primitiva ricorsiva.
\end{esempio}
%
%
\begin{esempio}[] $f(\overrightarrow{x},y)= \sum_{i=0}^{y} g(\overrightarrow{x},
i)$ con $g$ primitiva ricorsiva, allora:\\
$ f(\overrightarrow{x}, 0) = g(\overrightarrow{x}, 0) $ \\
$ f(\overrightarrow{x}, s(y)) = f(\overrightarrow{x}, y) +
g(\overrightarrow{x}, s(y)). $
\end{esempio}
%
%
\begin{esempio}[] $f(\overrightarrow{x},y)= \prod_{i=0}^{y}
g(\overrightarrow{x}, i)$ con $g$ primitiva ricorsiva, allora:\\
$ f(\overrightarrow{x}, 0) = g(\overrightarrow{x}, 0) $ \\
$ f(\overrightarrow{x}, s(y)) = f(\overrightarrow{x}, y) \cdot
g(\overrightarrow{x}, s(y)). $
\end{esempio}
%
%
\begin{esempio}[] $ \chi_{\geq}(x,y) = \left\{ \begin{array}{ll}
1 \ se \ x \geq y\\
0 \ altrimenti
\end{array}\right.$
\[ \chi_{\geq}(x,y) = sgn (s(x) \stackrel{\centerdot}{-} y) . \]
\end{esempio}
%
%
\begin{esempio}[valore assoluto] Basta scriverlo come
\[ \left|x - y \right| = (x \stackrel{\centerdot}{-} y) + ( y
\stackrel{\centerdot}{-} x) \]
oppure
\[ \left|x - y \right| = (x \stackrel{\centerdot}{-} y) \chi_{\geq}(x,y) +
( y \stackrel{\centerdot}{-} x) (1 - \chi_{\geq}(x,y)) .\]
\end{esempio}
%
%
\begin{esempio}[] $f(\overrightarrow{x}) = \left\{ \begin{array}{ll}
g_1(\overrightarrow{x})\ se\ vale\ R(\overrightarrow{x})\\
g_2(\overrightarrow{x})\ altrimenti
\end{array}\right.$ con \\$\chi_R(\overrightarrow{x}) = \left\{
\begin{array}{ll}
1\ se\ R(\overrightarrow{x})\ vale\\
0\ altrimenti
\end{array}\right.$ con $g_1(\overrightarrow{x})$, $g_2(\overrightarrow{x})$
e $\chi_R(\overrightarrow{x})$ primitive ricorsive.\\
Allora
\[ f(\overrightarrow{x}) = g_1(\overrightarrow{x}) \chi_R(\overrightarrow{x}) +
g_2(\overrightarrow{x}) (1 - \chi_R(\overrightarrow{x})). \]
\end{esempio}
%
% Alessandro Onnivello
\section{Dalle funzioni primitive ricorsive alle funzioni ricorsive}
Nel paragrafo precedente abbiamo definito l'insieme delle funzioni
primitive ricorsive; possiamo dire che queste siano sufficienti per
rappresentare tutte le funzioni calcolabili da una macchina di Turing?
La risposta è negativa, come andremo ora a dimostrare.
\begin{prop}\label{PRsonoTotali}
Le funzioni primitive ricorsive sono totali.
\end{prop}
\begin{proof}
Si dimostra per induzione sulla struttura delle funzioni primitive
ricorsive. Poichè le funzioni di base sono funzioni totali dobbiamo
verificare che la composizione e la ricorsione primitiva preservano la
totalità delle funzioni composte.
Per la composizione dobbiamo verificare che la funzione composta
$f(g_1, \dots, g_k)$ è totale. Per ipotesi induttiva
(strutturale) $f,g_1,\dots,g_k$ sono totali. Per la totalità di
$g_1,\dots,g_k$ tutti gli argomenti di $f$ sono definiti e, poichè $f$
è totale, anche la composizione è definita.
Per la ricorsione primitiva dobbiamo dimostrare che
$h:\mathbb{N}^{k+1} \to \mathbb{N}$ è definita $\forall y \in
\mathbb{N}$ e $\forall \vec{x} \in \mathbb{N}^k$.
$$h(\vec{x},y)=\left\{
\begin{array}{ll} h(\vec{x}, 0) = f(\vec{x})\\
h(\vec{x}, s(y)) = g(\vec{x},y,h(\vec{x},y))
\end{array} \right.$$
Si dimostra per induzione su $y$.
$y = 0$: $h(\vec{x}, 0) = f(\vec{x})$\\ $f$ è totale per ipotesi
induttiva quindi $\forall \vec{x} \in \mathbb{N}^k (\vec{x}, 0) \in
dom(h)$ come atteso
$y + 1$: $h(\vec{x}, y + 1) = g(\vec{x}, y, h(\vec{x}, y))$\\
$h(\vec{x}, y)$ è totale per ipotesi induttiva su $y$ quindi
$h(\vec{x}, y)$ è definita. Poichè $g$ totale per ipotesi induttiva e
$h(\vec{x}, y)$ è definita allora $\forall \vec{x} \in \mathbb{N}^k \;
(\vec{x}, y + 1) \in dom(h)$.
\end{proof}
Ora, per quanto detto nella precedente proposizione, l'insieme delle funzioni
primitive ricorsive permette di rappresentare solo funzioni calcolabili totali
ma, come abbiamo visto nel primo capitolo, le macchine di Turing possono
calcolare anche funzioni parziali. Quindi possiamo affermare che le
funzioni primitive ricorsive non rappresentano tutte le funzioni Turing
calcolabili.
Ma limitandoci al caso delle funzioni totali, possiamo dire che queste
siano tutte primitive ricorsive? Anche in questo caso la risposta è
negativa come vedremo nel prossimo paragrafo.
\section{Metodo diagonale di Cantor}
Per poter dimostrare che l'insieme delle funzioni primitive ricorsive
non contiene tutte le funzioni totali calcolabili andremo ora a
introdurre il metodo diagonale (o diagonalizzazione) di Cantor. Il
metodo di diagonalizzazione è una tecnica dimostrativa, ideata da
Georg Cantor per provare la non numerabilità dei numeri reali, che
risulta molto utile anche nell'ambito della logica matematica e della
teoria della computabilità come vedremo in seguito.
Il metodo diagonale di Cantor consiste nel costruire una funzione $\chi$
che differisce da un'insieme infinito di funzioni $\chi_0,\chi_1,\dots$
effettivamente enumerabile. Si costruisce quindi la seguente tabella
dove ogni colonna contiene una funzione unaria e le righe contengono
la sequenza dei naturali, ovvero tutti i possibili argomenti.
\begin{table}[!h]
\begin{center}
\begin{tabular}{|c|c|c|c|c}
\hline
& $\chi_0$ & $\chi_1$ & $\chi_2$ & $\ldots$\\
\hline
0 & $\chi_0(0)$ & $\chi_1(0)$ & $\chi_2(0)$ & $\ldots$\\
\hline
1 & $\chi_0(1)$ & $\chi_1(1)$ & $\chi_2(1)$ & $\ldots$\\
\hline
2 & $\chi_0(2)$ & $\chi_1(2)$ & $\chi_2(2)$ & $\ldots$\\
\hline
$\vdots$ & $\vdots$ & $\vdots$ & $\vdots$ & $\ddots$\\
\end{tabular}
\end{center}
\caption{Metodo diagonale di Cantor}
\end{table}
A questo punto possiamo costruire una funzione $\chi$ in modo tale che
differisca da ogni funzione enumerata nella tabella; vogliamo quindi
costruire una funzione tale che $\forall i \; \chi(i) \neq
\chi_{i}(i)$, ovvero che differisce da ogni altra funzione elencata
nella tabella almeno sulla diagonale. Questa nuova funzione, creata
tramite una procedura effettiva, non può appartenere all'insieme di
partenza, poichè altrimenti differirebbe da sè stessa sulla diagonale.
\begin{prop}\label{TotCalcNonNum}
Data una qualunque lista \emph{effettiva} di tutte le funzioni totali
calcolabili, unarie per semplicit\`a, $f_{0},\; f_{1},\; f_{2},
\cdots$ esiste sempre una funzione $g$ totale calcolabile che non
compare nella lista.
\end{prop}
\begin{proof}
Sia $f_{0},\; f_{1},\; f_{2}, \cdots$ una lista effettiva di funzioni
totali calcolabili, allora costruiamo la funzione $g$ nel seguente
modo:
\begin{center}
$g: \mathbb{N} \to \mathbb{N}$\\
$g(x) = f_{x}(x) + 1$
\end{center}
\begin{table}[!h]
\begin{center}
\begin{tabular}{|c|c|c|c|c}
\hline
& $f_0$ & $f_1$ & $f_2$ & $\ldots$\\
\hline
0 & $f_0(0) \mathbf{+1}$ & $f_1(0)$ & $f_2(0)$ & $\ldots$\\
\hline
1 & $f_0(1)$ & $f_1(1) \mathbf{+1}$ & $f_2(1)$ & $\ldots$\\
\hline
2 & $f_0(2)$ & $f_1(2)$ & $f_2(2) \mathbf{+1}$ & $\ldots$\\
\hline
$\vdots$ & $\vdots$ & $\vdots$ & $\vdots$ & $\ddots$\\
\end{tabular}
\end{center}
\caption{Funzione di diagonalizzazione $\mathbf{g}$}
\label{diagG}
\end{table}
$g$ è sicuramente totale calcolabile poichè, essendo la lista di
funzioni effettiva è quindi possibile, dato un qualsiasi $n \in
\mathbb{N}$, calcolare l'ennesima funzione, valutarla in $n$ ed
aggiungerci 1. È chiaro che $g$ non può appartenere alla lista, poichè
presa una qualsiasi colonna $n$ della tabella~\ref{diagG} che
rappresenta l'immagine dell'ennesima funzione, alla riga ennesima
$g(n) \neq f_{n}(n)$ poichè per definizione $g(n)=f_{n}(n)+1$. Perciò
non esiste alcun $n$ tale che $f_{n}(x)=g(x)$ per ogni $x \in
\mathbb{N}$ e quindi $g$ non è contenuta nella nostra lista.
\end{proof}
Fatto questo ragionamento, ne consegue che non è possibile enumerare
in modo effettivo tutte le funzioni totali calcolabili.
Possiamo usare ora la proposizone~\ref{TotCalcNonNum} per dimostrare il seguente
\begin{thm}\label{diagRic} Esiste una funzione totale calcolabile che non
è primitiva ricorsiva.
\end{thm}
\begin{proof}
Dobbiamo innanzitutto costruire una lista \emph{effettiva} di tutte le
funzioni primitive ricorsive (per semplicit\`a, supponiamole ad un
argomento).
Cerchiamo di scrivere informalmente un algoritmo che ci permetta di
listare tutte queste funzioni (in seguito vedremo come si pu\`o fare
in modo rigoroso). Una tale enumerazione si pu\`o ottenere nello
stesso modo in cui si ottiene una lista di teoremi a partire dagli
assiomi di una teoria. Si prende prima una funzione base, poi si
``contano'' tutte le funzioni ottenute da questa mediante una sola
applicazione di composizione e/o ricorsione. Poi si prende la seconda
funzione e tutte le funzioni derivate mediante una applicazione delle
due regole da quest'ultima e/o dalle funzioni gi\`a ottenute al passo
precedente, e cos\`i via. In questa maniera non si tralascia alcuna
funzione e ad ogni passo la lista \`e finita. Abbiamo cos\`i ottenuto
una enumerazione effettiva per tutte le funzioni dell'insieme
$\mathcal{PR}$.\\ Ma per quanto detto nella
proposizone~\ref{TotCalcNonNum} sappiamo che le funzioni totali non
sono enumerabili effettivamente quindi questo implica che esiste una
funzione totale calcolabile che non appartiene l'enumerazione che
abbiamo appena definito e che quindi non appartiene all'insieme
$\mathcal{PR}$.
\end{proof}
A questo punto ci chiediamo: cosa ci manca per avere tutte le funzioni
calcolabili?
Ripensando a quanto visto fino ad ora, il problema pu\`o stare o nel pretendere
di avere una lista effettiva delle funzioni calcolabili (cosa che per\`o \`e
ragionevole a farsi: lo abbiamo visto poco fa), o nell'imporre la propriet\`a di
totalit\`a alle funzioni. Dunque vediamo se lasciando cadere questa assunzione
riusciamo a classificare tutte le funzioni calcolabili.
\section{Le funzioni ricorsive}
Avendo osservato che le funzioni primitive ricorsive non
esauriscono tutte le funzioni calcolabili, il passo successivo \`e quello di
estenderle ulteriormente. In particolare ci sar\`a bisogno di trovare
anche funzioni parziali nella nostra definizione di ``ricorsivit\`a''. Perci\'o
introduciamo
la seguente funzione: \\
f. \textbf{minimizzazione}: sia $f: \mathbb{N}^{d+1} \to \mathbb{N}$ (anche
parziale)\\
definiamo la funzione di minimizzazione $h: \mathbb{N}^{d} \to \mathbb{N}$ come
$$ h(\vec{x}):= \mu_{y}(f(\vec{x},y)) $$
dove
\[\mu_{y}(f(\vec{x},y)) =
\begin{cases}
il \; minimo \; y \; tale \; che: \\
\qquad (i) \; f(\vec{x},z)\downarrow \; \forall z \leq y \; e \\
\qquad (ii) \; f(\vec{x},y)=0 & \text{se y esiste,} \\
non \; definita \; & \text{se $\exists z < y \; f(\vec{x},z) \uparrow$}\\
& \text{o se $f(\vec{x},y)\neq 0 \; \forall y \in \mathbb{N} $}
\end{cases} \]
con $\mu$ che prende il nome di operatore di minimizzazione.
\begin{thm} Le funzioni ricorsive sono b-programma-calcolabili.
\end{thm}
\begin{proof} Poich\`e abbiamo gi\`a dimostrato che le tre funzioni di base
(funzione zero, successore e proiezioni), la composizione generalizzata e la
ricorsione primitiva sono calcolabili da un b-programma ci resta solo da far
vedere che questo vale anche per la minimizzazione. Infatti se
$f:\mathbb{N}^{d+1} \to \mathbb{N}$ \`e computabile con un b-programma $\alpha$
allora possiamo eseguire la seguente assegnazione:
$$x_i \leftarrow f(\vec{x_{j}}, x_k)$$
(dove con $\vec{x_{j}}$ vogliamo rappresentare in modo compatto una lista di d
variabili di input)che significa "`dai come input $\vec{x_{j}}$ e $x_k$ al
b-programma $\alpha$ che calcola $f(\vec{x_{j}}, x_k)$ e dai output risultante
come valore a $x_i$"'. Dunque il b-programma che calcola la funzione di
minimizzazione su $\vec{x}$ \`e il seguente:\\
\begin{mylisting}
$x_0 \leftarrow 0$ \\
$loop$: $\vec{x_{1}} \leftarrow \vec{x}$\\
$x_{d+2} \leftarrow f(\vec{x_{1}}, x_0)$\\
if $x_{d+2} = 0$ then goto $stop$\\
$x_0 \leftarrow x_0 + 1$\\
goto $loop$
\end{mylisting}
\end{proof}
Una volta dimostrato che la minimizzazione è calcolabile dai
b-programmi possiamo dare la seguente
\begin{defi} Una funzione $f:\mathbb{N}^{d} \to \mathbb{N}$ (totale o
parziale) si dice \emph{ricorsiva} se si ottiene dalle funzioni
iniziali (a.--c.) applicando la composizione, la ricorsione e la
minimizzazione (d.--f.).
\end{defi}
Una cosa importante da notare \`e che l'utilizzo dell'operatore $\mu$ consente
di ottenere funzioni parziali anche a partire funzioni totali, come vedremo nei
seguenti esempi.
\begin{esempio} data $f:\mathbb{N} \to \mathbb{N}$ definita nel seguente modo:
$$f(x)= \left\{ \begin{array}{ll}
il \; min \; y \; t. \, c. \; x+y=0 & se \; \exists \; y\\
non \; definita & se \; x+y \neq 0 \; \forall y \in \mathbb{N}\\
\end{array} \right.$$
utilizzando la minimizzazione pu\`o essere scritta nel seguente modo:
$$f(x)= \mu_{y}(x+y)$$
Si noti che, essendo $x \in \mathbb{N}$, se $x \neq 0 \; f$ non \`e definita
anche se la funzione somma su cui viene applicato l'operatore di minimizzazione
\`e totale.
\end{esempio}
\begin{esempio} data $f:\mathbb{N}^{2} \to \mathbb{N}$ definita nel seguente
modo:
$$f(x,y)= \left\{ \begin{array}{ll}
x/y & se \; y \vert x\\
non \; definita & se \; y \nmid x\\
\end{array} \right.$$
pu\`o essere scritta nel seguente modo:
$$f(x,y) = \mu_{z}(\vert (z*y) - x \vert) $$
anche in questo caso la funzione $g(x,y,z)=\vert (z*y) - x \vert$ \`e totale, ma
combinata con l'operatore di minimizzazione consente di ottenere la funzione
parziale $f$.
\end{esempio}
\section{Relazioni ricorsive}
\begin{defi} $R \subseteq \mathbb{N}^{n}$ è una \emph{relazione
ricorsiva} se esiste una funzione ricorsiva $\chi_R$ che assume solo valori 0 e
1 e che soddisfa
$$\chi_R(x_1, \ldots, x_n):= \left \{ \begin{array}{ll}
1 & (x_{1}, x_{2}, \cdots, x_{n}) \in R\\
0 & (x_{1}, x_{2}, \cdots, x_{n}) \not \in