PARTE IV FUNZIONI RICORSIVE E LINGUAGGI FUNZIONALI

PARTE IV
FUNZIONI RICORSIVE
E LINGUAGGI FUNZIONALI
Funzioni ricorsive
Accenno al lambda-calcolo
Formalismo di Mc Carthy e linguaggi funzionali
1
4.1 FUNZIONI RICORSIVE
Tra gli approcci sviluppati negli anni ’30 per definire il concetto
di algoritmo un ruolo interessante hanno le funzioni ricorsive,
introdotte da S. Kleene nel 1936.
Anche in questo caso i risultati hanno portato una conferma alla
Tesi di Church-Turing. Lo stesso Kleene ha subito dimostrato
che la classe di funzioni definibili come funzioni ricorsive
coincideva con la classe delle funzioni calcolabili secondo
Turing e con la classe delle funzioni definibili con il lambdacalcolo.
2
Nel seguito considereremo solo funzioni del tipo Nx...xN → N
Def. La classe delle funzioni ricorsive primitive è la (più piccola)
classe di funzioni che contiene le
funzioni base:
- zero, cioè le funzioni O(n) (x1, ... , xn) = 0
nel caso n = 0 la funzione O(0) ( ) corrisponde alla costante 0
- successore, cioè la funzione S (x) = x + 1
- selettive (o identità), cioè le funzioni
Ui(n) (x1, ... , xn) = xi
3
ed è chiusa rispetto alle operazioni di
- composizione: date le funzioni g1, ..., gm di n argomenti e la
funzione h di m argomenti definiamo
f(x1, ... , xn) = h (g1(x1, ... , xn), ... , gm (x1, ... , xn))
- ricursione primitiva: date le funzioni g, di n argomenti e h di
n+2 argomenti definiamo
f(x1, ..., xn, 0) = g(x1, ... , xn)
f(x1, ..., xn, y+1) = h(x1, ... , xn, y, f(x1, ... , xn, y))
4
ESEMPI
Nota bene. I numeri naturali si ottengono come costanti
utilizzando le funzioni base e la composizione nel seguente
modo:
O(0) , S(O(0)), S(S(O(0))), S(S(S(O(0)))), ...
Inoltre possiamo utilizzare le funzioni base e la composizione
per definire ad esempio le funzioni di più argomenti ma con
valore costante:
uno (x) = S(O(1)(x)), due (x) = S(S(O(1)(x))), ecc.
oppure le funzioni che incrementano un valore dato:
piùdue (x) = S(S(x)), piùtre (x) = S(S(S(x))), ecc.
5
Per definire altre funzioni usiamo la ricursione primitiva
Possiamo ad esempio dimostrare che la somma è una funzione
ricorsiva primitiva:
somma (x, 0) = U1(1) (x)
somma (x, y+1) = S(U3(3) (x, y, somma (x, y)))
Cioè in questo caso le funzione g ed h che compaiono nello
schema della ricursione primitiva (e che devono già essere
ricorsive primitive) sono rispettivamente la funzione U1(1) e una
funzione ottenuta dalla composizione di S e U3(3).
Più semplicemente potremo scrivere:
somma (x, 0) = x
somma (x, y+1) = S(somma (x, y))
6
ALTRI ESEMPI
predecessore (0) = O(0) = 0
predecessore (y+1) = U2(3) (x, y, predecessore (y)) = y
sub (x, 0) = U1(1) (x) = x
sub (x, y+1) = predecessore (U3(3) (x, y, sub (x, y)))
= predecessore (sub (x, y))
prodotto (x, 0) = 0
prodotto (x, y+1) = somma (x, prodotto (x, y))
esp (x, 0) = 1
esp (x, y+1) = prodotto (x, esp (x, y))
7
Poiché operiamo solo con interi non negativi dobbiamo
convenire che:
0–1=0
e che
a – b = 0 se a ≤ b
Esercizio. Supponendo che le funzioni g(x) e h(x) siano ricorsive
primitive mostrare che la seguente funzione è ricorsiva primitiva:
f(x, y) = g(x) se x ≤ y
= h(x) se x > y
8
Si noti che la ricursione primitiva può essere realizzata in un
linguaggio di programmazione con il solo costrutto ‘for’.
9
Le funzioni ricorsive primitive non esauriscono l’insieme delle
funzioni calcolabili:
1.innanzitutto sono tutte funzioni totali e noi sappiamo che tra le
funzioni calcolabili ci sono necessariamente funzioni parziali,
ad esempio la funzione
f(dM, x) = 1 se la macchina di Turing M si arresta su input x,
indefinito altrimenti
2.in secondo luogo esistono funzioni definite mediante
meccanismi di ricursione più complessi della ricursione
primitiva (ad es. ricursione doppia) che sono calcolabili
mediante algoritmi ma non ricorsive primitive
10
Consideriamo la seguente funzione:
torre (x, 0) = 1
torre (x, y+1) = esp (x, torre (x, y))
Si noti che la funzione torre ha valori che crescono molto
rapidamente. Ad esempio
torre (3, 4) = esp (3, esp (3, esp (3, 3))) =
esp (3, esp (3, 27)) = esp (3, 7 x 1012)) = esp (37,1012))
che è maggiore di esp (220,1012)) > 10 6000 miliardi
Nota che il numero di atomi dell’universo è inferiore a 10100.
Il procedimento che ci ha fatto passare dall’esponenziale alla
torre può essere ripetuto all’infinito.
11
Funzione di Ackermann.
Consideriamo le seguenti funzioni:
f0 (x, y) = x+y
f1 (x, y) = x y
e, per n ≥ 2
fn (x, y) = se y=0 allora 1 altrimenti
fn-1(x, fn (x, y-1))
Esse sono tutte ricorsive primitive ma se le consideriamo come
una unica funzione di tre variabili ciò non è più vero.
f(n, x, y) = se n = 0 allora x+y
altrimenti se n = 1 allora xy
altrimenti se y = 0 allora 1
altrimenti f(n-1, x, f(n, x, y-1))
12
Teorema La funzione A(z) = f(z, z, z) (funzione di Ackermann)
non è ricorsiva primitiva
Cenno di dimostrazione. Si può dimostrare che essa cresce più
rapidamente di ogni funzione ricorsiva di una variabile.
Tuttavia la funzione di Ackermann è chiaramente calcolabile.
Per definire le funzioni calcolabili gli operatori di composizione e
ricursione primitiva non sono sufficienti. E’ necessario introdurre
un’ulteriore operazione per definire una classe di funzioni più
ampia.
13
Def. La classe delle funzioni ricorsive generali è la (più piccola)
classe di funzioni che contiene le
funzioni base ed è chiusa rispetto alle operazioni di
- composizione
- ricursione primitiva
- minimalizzazione: data la funzione g di n+1 argomenti
possiamo definire
f(x1, ... , xn) = µt (g(x1, ... , xn, t) = 0)
che si interpreta nel seguente modo: il valore di f è il minimo
valore di t che soddisfa il predicato g(x1, ... , xn, t) = 0 (se per
nessun valore di t accade che g(x1, ... , xn, t) = 0 allora
f(x1, ... , xn) non è definita.
14
NOTA BENE Per comodità possiamo usare una formulazione
leggermente modificata dell’operazione di minimalizzazione.
Anzichè porre come condizione da verificare il predicato
g(x1, ..., xn, t) = 0 per una opportuna funzione g, possiamo
utilizzare più in generale un predicato del tipo
s (x1, ..., xn, t) > r (x1, ..., xn, t) dove s e r sono funzioni ricorsive.
Nota che il predicato a = b può essere espresso anche come
g (a, b) = 0 se si definisce g (a, b) = (a – b) + (b – a)
15
Esempio
(√x) = µt ((t+1)(t+1) > x)
x=1
t=0
t=1
(t+1)(t+1) = 1
(t+1)(t+1) = 4
x=2
t=0
t=1
(t+1)(t+1) = 1
(t+1)(t+1) = 4
x=3
t=0
t=1
(t+1)(t+1) = 1
(t+1)(t+1) = 4
x=4
t=0
t=1
t=2
(t+1)(t+1) = 1
(t+1)(t+1) = 4
(t+1)(t+1) = 9
16
Si noti che l’operazione di minimalizzazione (corrispondente al
costrutto ‘while ... do ...’ o a costrutti iterativi simili presenti nei
linguaggi di programmazione) è molto comoda anche per
definire funzioni che (come nel caso della parte intera della
radice) sono in realtà ricorsive primitive.
A tal fine è utile introdurre una variante dell’operazione di
minimalizzazione, chiamata µ-limitato.
Teorema Siano h e g funzioni ricorsive primitive; la funzione
così definita (µ-limitato):
f(x1, ... , xn) = µt ≤ h(x1, ... , xn)(g(x1, ... , xn, t) = 0)
= µt (g(x1, ... , xn, t) = 0)
se esiste t ≤ h(x1, ... , xn) tale che
g(x1, ... , xn, t) = 0, 0 altrimenti
è ricorsiva primitiva.
17
Esercizio Dimostrare il teorema precedente.
Suggerimento: realizzare prima i seguenti esercizi. Data una
funzione g(t) che sicuramente assume valore 0 nell’intervallo
[0, T], definire le seguenti funzioni:
g’(t)
= 0 se g(t) > 0
= 1 se g(t) = 0
g’’(t) = t’ se g(t’) = 0 e per ogni t<t’ g(t) > 0
= 0 altrimenti
18
Si può facilmente verificare che le funzioni ricorsive sono
calcolabili mediante macchine di Turing o mediante macchine a
registri. Più difficile è mostrare il risultato opposto e cioè:
Teorema. Ogni funzione calcolabile con una macchina a registri
è ricorsiva.
Dim. Per semplificare la dimostrazione possiamo partire, anziché
dalle macchine a registri, dalle macchine a registri elementari e
mostrare che la funzione calcolata da una macchina a registri
elementare è ricorsiva.
Ricordiamo che una MREL viene inizializzata con i valori x1, ...,
xn nei registri R1, ..., Rn e quando termina (una MREL termina
se esegue un salto all’istruzione 0) il valore del risultato è
contenuto nel registro R1.
19
Osserviamo innanzitutto che la configurazione di una MREL, è
completamente determinata se forniamo il contenuto dei suoi
registri e il contenuto del ‘program counter’, cioè il numero
d’ordine della prossima istruzione da eseguire.
Per trasformare tali valori in numeri interi gestibili da una MREL
possiamo adottare le seguenti codifiche.
Data una sequenza di interi x1, ..., xn denotiamo con < x1, ..., xn>
l’intero 2x13x25x3 ... pnxn
Naturalmente dato un intero z = < x1, ..., xn>
i valori di x1, ..., xn si possono recuperare mediante la
decomposizione in fattori primi.
Indichiamo con (z)k l’esponente del primo pk nella
decomposizione di z.
Se pn è il massimo numero primo che divide z, abbiamo
evidentemente
20
z = <(z)1, ..., (z)n>
Utilizzando tale codifica possiamo codificare il contenuto dei
registri e la configurazione di una MREL nel seguente modo:
c(registri) = <cont(R1), ..., cont(Rn)>
c(configurazione) = <cont(PC), c(registri)>
ed una computazione eseguita da una MREL, cioè una sequenza
di configurazioni c0, c1, ..., ck, come
c(computazione) = <c(c0), c(c1), ..., c(ck)>
A questo punto, dato un programma π per MREL si può definire
un predicato Tπ che dato un intero t ed una n-pla di interi x1, ...,
xn, verifica se t sia il codice di una computazione legale
compiuta dal programma π con input x1, ..., xn.
21
Il predicato assume quindi valore vero se e solo se:
- (t)0 è la codifica della configurazione iniziale
- (t)n è la codifica della configurazione finale se e solo se pn è il
massimo numero primo che divide t e il PC contiene 0
- per ogni i ≥ 0, (t)i+1 è correttamente la configurazione seguente
alla configurazione (t)i in base a quanto previsto dalle
istruzioni del programma π.
Supponendo che un intero t soddisfi il predicato Tπ, per il dato
input x1, ... , xn, il valore della funzione calcolata dal programma
π sarà recuperabile facilmente dalla configurazione (t)n poiché
non è altro che il contenuto del registro R1 nella configurazione
finale della computazione, e cioè il valore
Uπ(t) = (((t)n)2)1
In definitiva avremo che la funzione f calcolata dalla MREL
soddisfa la seguente forma normale (Forma Normale di Kleene):
f(x1, ... , xn) = Uπ (µt (Tπ (x1, ... , xn, t)))
22
4.2 LAMBDA CALCOLO
Il lambda calcolo (introdotto dal logico americano A. Church negli
anni 1932-36) costituisce un modello formale che ha un ruolo
importante nello studio dei fondamenti della programmazione. Infatti
esso ha determinato l’introduzione di una notazione per la
rappresentazione di funzioni nonché altri importanti concetti di
programmazione ed è stato utilizzato per lo studio delle proprietà
semantiche dei programmi.
Notazione lambda
Sia data l’espressione x + y2. A seconda di come la si considera essa
può assumere diversi significati.
- se x ed y sono variabili di tipo intero di cui assumiamo fissato il
valore, l’espressione corrisponde al valore intero z = x + y2, il suo
tipo è quindi il tipo ‘intero’, cioe’ N;
23
- se x ed y sono variabili di tipo intero ma di esse non e’ fissato il
valore, l’espressione corrisponde ad una regola che consente di
associare ad ogni coppia di valori x ed y il valore x + y2. Il suo tipo
è quindi quello di una funzione intera di due argomenti, cioè
NxN→N
- se assumiamo che x ed y siano due variabili ma che i loro valori
siano noti in momenti successivi l’espressione può essere
considerata una regola che mi permette di associare ad ogni valore
di x una funzione (del solo argomento y) che a sua volta mi
permetterà di associare ad ogni valore y, il valore x + y2. Il suo tipo
è dunque N→ (N → N)
24
La notazione lambda mi permette di distinguere i tre casi scrivendo
rispettivamente:
x + y2
λ(x, y). x + y2
λx. λy. x + y2
La notazione lambda corrisponde alla dichiarazione del tipo di una
funzione e dei suoi parametri formali.
25
NOTA BENE La seconda e la terza espressione sono formalmente
distinte ma sostanzialmente equivalenti, in quanto ogni funzione di
due argomenti può essere considerata come una infinità di funzioni
di un solo argomento (il secondo) per le quali il primo argomento
costituisce un parametro.
Nel caso in oggetto abbiamo le infinite funzioni (della sola variabile
y)
λy. 0 + y2
λy. 1 + y2
...
λy. k + y2
...
Quando si determina il valore k della variabile x automaticamente
(con un procedimento di valutazione parziale) selezioniamo la
funzione di una sola variabile λy. k + y2 il cui valore sarà poi
determinato al momento della conoscenza del valore della y.
26
La trasformazione dalla seconda alla terza forma si chiama
Currizzazione, dal nome del logico Curry che ne ha studiato le
proprietà.
In realtà la notazione lambda non è solo una notazione ma pone le
premesse per un vero e proprio sistema di calcolo.
27
Formule.
Chiamiamo lambda-formule le espressioni così definite:
- una variabile x, y, z, ... è una lambda-formula
- se A e B sono lambda-formule l’espressione (AB) è una lambdaformula (applicazione)
- se x è una variabile ed A è una lambda formula, l’espressione
B = λx. A è una lambda-formula (lambda-astrazione). La variabile
x si dice che occorre legata nella formula B.
Le variabili legate hanno lo stesso ruolo dei parametri formali di una
funzione in un programma.
Si noti inoltre che possiamo sempre attribuire un nome (ad es. A) ad
una formula (ad esempio la formula (λx. (λy. ((x y) y)))
28
Regole del calcolo (riduzioni).
α-riduzione (ridenominazione delle variabili legate). se una variabile
occorre legata in una formula A essa può essere ridenominata con
una variabile che non occorre in A.
β-riduzione. Se la formula λx. A si applica ad una formula B l’effetto
della β-riduzione è di sostituire B in ogni occorrenza di x in A
(denotato con A [x/B]), curando che nessuna variabile libera in B
diventi una variabile legata in A [x/B]
η-riduzione. Se nella formula λx. Bx la variabile x non occorre in B
la formula λx. Bx si riduce a B.
29
Esempi
Il primo esempio di utilizzazione delle β-riduzioni (esempio
informale, perché fa uso in modo misto della notazione lambda e di
usuali espressioni algebriche) è il seguente. Data la formula:
(((λx. λy. x + y2) 3) 4) essa si riduce prima a
((λy. 3 + y2) 4) e poi a 3 + 42
Un altro esempio, più astratto, è il seguente:
((λx. (λy. ((x y) y))) λu. u w) si riduce a
λy. (((λu. u w) y) y) e poi a
λy. ((y w) y)
30
Il seguente esempio, una semplice variante del precedente, richiede
prima una α-riduzione:
((λx. (λy. ((x y) y))) λw. y w) -->
((λx. (λu. ((x u) u))) λw. y w)
perchè y è libera in λw. y w e diventerebbe legata a seguito della
sostituzione di λw. y w al posto di x. Si ottiene così
λu. (((λw. y w) u) u) e poi λu. ((y u) u)
31
Prima di proseguire osserviamo che per alleggerire la notazione
possiamo introdurre le seguenti proprietà di associatività:
- l’applicazione associa a sinistra, cioè la formula
((x y) z) può essere scritta anche x y z
- la lambda astrazione associa a destra, cioè la formula
(λx. (λy. A)) può essere anche scritta λx. λy. A
32
Calcolo di funzioni numeriche mediante le regole del lambda-calcolo
Per poter definire funzioni numeriche assumiamo innanzitutto che
alcune (una infinità) di formule vengano interpretate come
rappresentazioni dei numeri naturali. Esistono vari sistemi di
rappresentazione. Il più classico e comodo è il seguente:
0 -->
1 -->
2 -->
3 -->
....
n -->
....
λx. λy. y
λx. λy. xy
λx. λy. (x (xy))
λx. λy. (x (x (xy)))
λx. λy. (x (x (x ... (xy)...))
n volte
Le formule corrispondenti ai naturali sono rappresentate in genere
con il valore del naturale sottolineato: 0, 1, 2, ..., n, ...
33
Le seguenti proprietà sono facilmente dimostrabili:
(iterazione)
1fx = fx
n f x = f (f ( … (f x)…)) = f n x
n volte
Nota bene che, coerentemente, abbiamo
0 f x = f0 x = x e quindi se I = λx.x è la formula che svolge il ruolo
dell’identità abbiamo che per ogni f , 0 f = f0 = I
(esponenziale)
(composizione)
B f g x = f (g x)
n m = mn
Sia B = λx. λy. λz. (x (yz))
Bnm = nxm
34
Potremmo così proseguire definendo formule per tutte le funzioni
base, per gli operatori di ricursione e di minimalizzazione e quindi
ottenendo in definitiva il seguente risultato.
Teorema Il lambda-calcolo consente di calcolare tutte e sole le
funzioni calcolabili secondo Turing
Questo risultato, ottenuto da Turing nel 1936, è alla base della
affermazione (molto più forte e, chiaramente, indimostrabile)
chiamata tesi di Church o di Church-Turing:
Ogni funzione calcolabile mediante un qualunque sistema di calcolo
o un qualunque algoritmo è calcolabile secondo Turing.
35
4.3 FORMALISMO DI MC CARTHY E
LINGUAGGI FUNZIONALI
I linguaggi di programmazione funzionali devono la loro origine a
vari filoni culturali:
- all’approccio basato sulle funzioni ricorsive, come metodo per la
definizione del concetto di calcolabilità
- al lambda-calcolo, come notazione per la rapresentazione di
funzioni e come sistema di calcolo che prevede una notevole
flessibilità di meccanismi di valutazione (esecuzione) e prevede la
possibilità di elaborare oggetti di tipo diverso (dati, funzioni ecc.)
Anello di congiunzione tra questi filoni ed i veri e propri linguaggi
funzionali è il formalismo di Mc Carthy (definito a metà degli anni
‘50) e strettamente collegato al primo e più famoso dei linguaggi di
programmazione funzionali, il linguaggio LISP.
36
Il formalismo di Mc Carthy consente di definire la struttura sintattica
dei programmi (schemi di programmi). Le diverse interpretazioni dei
simboli utilizzati nel formalismo conducono a diversi possibili
linguaggi di programmazione.
Simboli disponibili
Υ = insieme contenente infiniti simboli di variabile, x0, x1, x2, ... , x, y,
z, ...
Β = insieme contenente infiniti simboli di funzioni base, b0, b1, b2, ...,
a, b, c, ...
Φ = insieme contenente infiniti simboli di funzioni variabili, F0, F1, F2,
..., F, G, H, ...
37
a: N --> N, funzione che determina la arità delle funzioni base
A: N --> N, funzione che determina la arità delle funzioni variabili
Ad esempio se a (1) = 3 vuol dire che la funzione base b1 ha tre
argomenti
Nota bene. Se la arità di una funzione base bi è 0, bi ha il ruolo di una
costante.
38
Termini su < Υ, Β, Φ >
Definizione induttiva:
- un simbolo di variabile è un termine;
- un simbolo di funzione base con arità 0 (una costante) è un
termine;
- se t1, ..., tn sono termini e la arità di bi è n allora
- bi (t1, t2, ..., tn) è un termine
- se t1, ..., tn sono termini e la arità di Fi è n allora
- Fi (t1, t2, ..., tn) è un termine.
Null’altro è un termine.
L’insieme dei termini su < Υ, Β, Φ > è denotato
Τ (Υ, Β, Φ ).
39
Se un termine non contiene variabili è chiamato termine chiuso (o
‘ground’, o di base).
L’insieme dei termini chiusi su <Υ, Β, Φ > è denotato con
Τ0 (Υ, Β, Φ ).
Esempi. Se a(0) = 0, a(1) = 2, a(2) = 1, A(0) = 2,
i seguenti sono termini in Τ (Υ, Β, Φ ):
b1 (b0, b0)
F0 (F0 (b0, b2 (b0)), b1 (b2 (b0), b0))
F0 (F0 (x, y), b1 (b2 (x), b0))
Il primo e il secondo sono termini chiusi.
40
Dichiarazioni e programmi su < Υ, Β, Φ >
Uno schema di programma su < Υ, Β, Φ > è un insieme di coppie
<Fi, ti> dove
Fi ∈ Φ e t i ∈ Τ (Υ, Β, Φ ),
seguito da un termine chiuso t0 ∈ Τ0 (Υ, Β, Φ ).
Ogni coppia <Fi, ti> costituisce di fatto una dichiarazione di funzione
e viene rappresentata nel seguente modo:
Fi ⇐ ti
41
Esempi
F (x) ⇐ b2 (x, b1 (b0), b0 (x, F (b1 (y))))
F (b1 (b0))
F (x, y) ⇐ b1 (F (x, b2 (y))
F (b1(b1(b0)), b2 (b0))
42
Interpretazione di schemi di programmi: il linguaggio SLF
Per passare da schemi di programmi a programmi (cioè per passare
dal formalismo di Mc Carthy ad un vero e proprio linguaggio di
programmazione) dobbiamo interpretare i simboli di Υ e Β;
l’interpretazione di Φ si otterrà di conseguenza.
43
Assumiamo quindi in particolare la seguente interpretazione:
- i simboli di variabile in Υ si interpretano come variabili intere;
- i simboli di funzione base in Β si interpretano come funzioni intere;
- in particolare assumiamo che Β sia costituito dalle seguenti
funzioni:
- per ogni numero naturale c, infinite funzioni costanti CC(n)
(x1, ... , xn) = c, di arità n ≥ 0 che restituiscono il valore intero
costante c;
per ogni c, anziché scrivere la funzione CC(0) utilizziamo il
corrispondente naturale c;
- S (x) = x + 1, il successore, di arità 1;
- P (x) = x - 1, il predecessore, di arità 1;
- if-then-else (x, y, z) = se x = 0 allora y,
altrimenti z.
44
Il linguaggio SLF (Semplice Linguaggio Funzionale) è l’insieme dei
programmi che si ottengono interpretando nel modo detto gli schemi
di programmi del formalismo di Mc Carthy.
Esempio: il programma che calcola il fattoriale di 4.
F1 (x, y) ⇐ if-then-else (y, x, S ( F1 (x, P (y))))
F2 (x, y) ⇐ if-then-else (y, 0, F1 ( x, F2 (x, P (y))))
F3 (x) ⇐ if-then-else (x, 1, F2 ( x, F3 (P (x))))
F3 (4)
45
Calcolabilità delle funzioni ricorsive generali in SLF.
Non è difficile mostrare che tutte le funzioni ricorsive sono
calcolabili in SLF.
Funzioni base.
Le funzioni base zero e successore sono disponibili come funzioni
base nel repertorio del linguaggio SLF.
Le funzioni selettive si possono calcolare con il programma:
Ui(n) (x1, ... , xn) ⇐ xi
46
Composizione.
Una funzione F definita per composizione della funzione h con le
funzioni g1, .. , gm si può definire in SLF come segue:
F (x1, .. , xn) ⇐ h (g1(x1, ... , xn), .. , gm (x1, .. , xn))
47
Ricursione primitiva
Una funzione F definita per ricursione primitiva a partire dalle
funzioni g ed h:
F (x1, ..., xn, 0) = g(x1, ... , xn)
F (x1, ..., xn, y+1) = h(x1, ... , xn, y, F (x1, ... , xn, y))
può essere definita in SLF come segue:
F (x1, ..., xn, y) ⇐ if-then-else (y, g(x1, ... , xn),
h (x1, ... , xn, P(y), F (x1, ... , xn, P(y))))
48
Minimalizzazione
Una funzione f definita per minimalizzazione a partire dalla funzione
g:
F (x1, ... , xn) = µt (g(x1, ... , xn, t) = 0)
può essere definita in SLF come segue:
F (x1, ... , xn) ⇐ G (x1, ... , xn, 0)
G (x1, ... , xn, t) ⇐ if-then-else (g(x1, ... , xn, t), t,
G (x1, ... , xn, S (t)))
49