7. Compléments sur la Récursivité
1 Exercice 7.3.1
2 Exercice 7.3.2
3 Exercice 7.3.3
4 Exercice 7.3.4
5 Exercice 7.3.5
6 Exercice 7.3.6
7 Exercice 7.3.7
8 Exercice 7.3.8
6.0.1.11

7. Compléments sur la Récursivité

1 Exercice 7.3.1

a) Un prédicat n’est autre qu’une fonction dont le résultat est booléen : ce ne peut être que true ou false.

ERRATUM : Le livre se trompe dans les exemples, la place du prédicat est incorrecte, il doit être en dernière position et non en première !

(define (every? a b pred)

  (or (> a b)

      (and (pred a)

           (every? (+ a 1) b pred))))   ; itératif...

Le cas a > b laisse certains étudiants perplexes. En fait, si a > b, l’intervalle [a,b] est vide, et les éléments de l’ensemble vide possèdent toutes les propriétés du monde. Ce sont par exemple tous des livres écrits en 1217, comme vous le vérifierez en raisonnant par l’absurde :-)

b) Les entiers étant petits, on peut utiliser le prédicat premier? de l’exercice 6.8.13 :

(every? 890 900 premier?)    ; --> #t

c) Analogue à la question a). Même ERRATUM sur la position du prédicat dans les exemples...

d) Erreur naïve : la négation d’un prédicat p n’est pas (not p) car p n’est pas un booléen. Attention au typage ! Il s’agit en fait de... de quoi ? Ce doit être un prédicat !

2 Exercice 7.3.2

a) Il suffit d’introduire une fonction locale (iter a acc) avec deux variables de boucle [les autres ne varient pas]. Attention, l’accumulateur acc est à gauche, ceci n’a d’importance que si l’opérateur op n’est pas commutatif.

b) En une ligne :

(define (integrale f a b dx)     ; a la Riemann

  (* dx (accumulation + 0 f a (lambda (x) (+ x dx)) b)))

3 Exercice 7.3.3

Je vous conseille, si vous êtes matheux, à jeter un oeil sur Mathematica, très beau système, hélas un peu onéreux...

a) Nest et FixedPoint sont des primitives de Mathematica...

(define (nest f x n)

  (if (= n 0)

      x

      (nest f (f x) (- n 1))))      ; iteratif...

> (nest (lambda (x) (/ (+ x (/ 2 x)) 2)) #i1.0 5)   ; calcul de (sqrt 2)

#i1.414213562373095

(define (fixed-point f x egaux?)

  (local [(define (iter xp x)       ; xp est le x precedent

            (if (egaux? xp x)

                x

                (iter x (f x))))]

    (iter x (f x))))

ERRATUM : l’exemple du livre a tort d’utiliser le signe =. Il n’y aura jamais stricte égalité. Il faut passer un prédicat d’égalité approchée :

(define (quasi=? x1 x2)

  (< (abs (- x1 x2)) #i0.00001))

> (fixed-point cos #i1.0 quasi=?)   ; point de départ arbitraire [maths...]

#i0.7390822985224023

c) x = 1 + 1/x <==> x2-x-1 = 0. On cherche la solution > 0.

> (fixed-point (lambda (x) (+ 1 (/ 1 x))) #i1.0 quasi=?)

#i1.6180322875418822

4 Exercice 7.3.4

Typiquement l’exercice qu’il faut faire seul pour être content de soi, alors je ne vais pas gâcher votre plaisir... On suppose que la dérivée de f ne s’annulle en aucun point dans le domaine d’étude [géométriquement, pas de tangente horizontale !]. D’où vient la transformation de l’approximation a en une meilleure approximation a - f(a)/f’(a) ? Simplement de l’équation de la tangente à la courbe de f au point d’abscisse a. La technique de Newton consistait à trouver l’intersection de cette tangente en M(a;f(a)) avec l’axe Ox, obtenant ainsi une approximation meilleure que a. La convergence vers une solution de f(x) = 0 est rapide.

5 Exercice 7.3.5

a) Exercice sur l’épluchage des chiffres d’un entier n par divisions successives... Le chiffre des unités en base b s’obtient par (modulo n b). Le nombre privé du chiffre des unités n’est autre que (quotient n b).

(define (somme-chiffres n b)

  (if (< n b)

      n

      (+ (modulo n b) (somme-chiffres (quotient n b) b))))

b) Je vous la laisse...

c) Il faut faire abstraction des calculs effectués dans les deux fonctions précédentes, et les passer en paramètres dans une version plus générale...

6 Exercice 7.3.6

Passer en CPS revient à prendre en main la suite des calculs. Nommons f la continuation courante. Il s’agit donc maintenant de calculer [de manière automatiquement itérative] l’image par f du nombre de serrements de main parmi n convives :

(define (k-serrements-de-main n f)    ; CPS

  (if (= n 0)

      (f 0)

      (k-serrements-de-main (- n 1)   ; itératif

        (lambda (r) (f (+ (- n 1) r))))))

> (k-serrements-de-main 10 add1)

46

b) Les continuations peuvent avoir plusieurs paramètres. Ici (k-bezout a b f) calcule f(g,u,v) où g est le PGCD de a,b et u,v des coefficients de Bezout : g = au+bv.

(define (bezout a b f)    ; CPS. Calcule (f g u v) avec g = au + bv

  (if (zero? b)

      (f a 1 0)

      (bezout b (modulo a b)

        (lambda (g1 u1 v1)

          (f g1 v1 (- u1 (* (quotient a b) v1)))))))

> (bezout 12 8 list)

(4 1 -1)                               ; 4 = 12*(1) + 8*(-1)

> (bezout 12 8 (lambda (g u v) g))     ; le pgcd seulement

4

c) Une fonction est itérative si et seulement si sa transformation en CPS laisse invariante la continuation courante :

(define (k-pgcd a b f)

  (if (zero? b)

      (f a)

      (k-pgcd b (modulo a b) f)))      ; f ne change pas !

7 Exercice 7.3.7

a) Je commence par définir la structure de couple. Le couple résultat n’est construit qu’à la fin.

(define-struct couple (premier second))

 

(define (division1 a b)

  (local [(define (iter a b q)     ; r n'est autre que a qui diminue...

            (if (< a b)

                (make-couple q a)

                (iter (- a b) b (+ q 1))))]

    (iter a b 0)))

> (division1 22 8)

#(struct:couple 2 6)

b) La récurrence enveloppée traîne les couples intermédiaires :

(define (division a b)

  (if (< a b)

      (make-couple 0 a)

      (local [(define HR (division (- a b) b))]

                (make-couple (+ 1 (couple-premier HR))

                             (couple-second HR)))))

> (division 22 8)

#(struct:couple 2 6)

c) La version CPS.

(define (k-division a b f)   ; calcule f(q,r) avec a = bq+r

  (if (< a b)

      (f 0 a)

      (k-division (- a b) b

        (lambda (q1 r1)

          (f (+ q1 1) r1)))))

> (k-division 22 8 make-couple)

#(struct:couple 2 6)

 

(define ($modulo a b)

  (k-division a b (lambda (q r) (zero? r))))

Dans le dernier exemple ci-dessus, on notera le style CPS, préférable à un style semi-direct :

(define ($modulo a b)

  (zero? (k-division a b (lambda (q r) r))))

8 Exercice 7.3.8

a) ERRATUM : la fonction à programmer est (k-fib n f) == (f (fib n) (fib (+ n 1)))

(define (k-fib n f)             ; calcule (f (fib n) (fib (+ n 1)))

  (if (= n 0)

      (f 0 1)

      (k-fib (- n 1) (lambda (a b) (f b (+ a b))))))

> (k-fib 200 (lambda (x y) x))

280571172992510140037611932413038677189525   ; Time = 0 ms

b) Passez directement en paramètres les entiers a = fib(n) et b = fib(n+1)...