Symbolic differentiation & numerical computing en pur LISP
Laurent Thiry — Programmation en C
« Un outil, c'est du code qui sert à écrire du code. »
— ou comment recycler son évaluateur en bibliothèque
./let en vrai outil.
Pas un PowerPoint. Pas une spec. Du code qui calcule.
grep, make, gitDéfinition : le code du programme est une structure de donnée du langage lui-même.
// Le code est du texte, point. // Analyser du C depuis du C ? // -> parser + AST ~ 10k lignes
Le C peut manipuler du texte, mais
le texte n'est pas du C exécutable.
;; Le code est une liste. (define (square x) (* x x)) ;; Je peux construire du code : (list 'define (list 'square 'x) (list '* 'x 'x))
Code et donnée sont la même chose.
Une liste. Un arbre.
;; Expression mathématique (+ 1 (* 2 3)) ;; Interprétée comme arbre : + / \ 1 * / \ 2 3
Un programme LISP est un arbre de syntaxe abstraite (AST) écrit en syntaxe concrète. Pas de parsing séparé : ce que vous tapez est l'arbre.
./let + 50 lignes de bibliothèque.
L'échelle diffère, pas le principe.
Vous utilisez des langages LISP sans le savoir. Regardez l'arbre généalogique :
Chaque cours vous a donné une pièce du puzzle. Voici comment elles s'assemblent :
| Cours | Concept | Utile pour |
|---|---|---|
| 1 | Variables, boucles, fonctions | Écrire les primitives C de l'évaluateur |
| 2 | VM à pile, bytecode | Comprendre l'exécution pas-à-pas |
| 3 | Parseur, s-expressions, compilateur | Lire les fichiers .lisp |
| 4 | Réseau, serveur HTTP | ℹ️ Contexte général |
| 5 | Évaluateur, environnements chaînés | Exécuter let, lambda, define |
| 6 | Persistance, prolog.lisp | Charger des bibliothèques .lisp |
| 7 | Bibliothèques LISP | 🚀 mathematica.lisp + matlab.lisp |
Calcul formel — arbres d'expression, dérivation symbolique, simplification algébrique.
;; d/dx de 3x + 1 (deriv-simp '(Add (Mult (Val 3) (Var x)) (Val 1)) 'x) ; → (Val 3)
50 lignes. Zéro dépendance.
Comme Mathematica, mais en moins cher.
Calcul numérique — vecteurs, matrices, produit scalaire, norme, transposition.
;; Produit matriciel (mulmat '((1 2) (3 4)) '((5 6) (7 8))) ; → ((19 22) (43 50))
150 lignes. Tout en pur LISP.
Même pattern que NumPy (C + Python).
./let '(load mathematica.lisp)' '(…)'
ou
./let '(load matlab.lisp)' '(…)'
| Fonction | Paramètres | Retourne | Exemple |
|---|---|---|---|
deriv |
expr var |
Dérivée brute | (deriv '(Mult (Val 2) (Var x)) 'x)→ (Add (Mult (Val 2) (Val 1)) (Mult (Val 0) (Var x))) |
simplify |
expr |
Expression simplifiée | (simplify '(Add (Val 0) (Var x)))→ (Var x) |
deriv-simp |
expr var |
Dérivée simplifiée | (deriv-simp '(Mult (Var x) (Var x)) 'x)→ (Add (Var x) (Var x)) |
Types de nœuds : Val (entier), Var (symbole), Add, Sub, Mult, Div.
$ ./let '(load mathematica.lisp)'
'(deriv-simp
(quote (Add (Mult (Val 3)
(Var x))
(Val 1)))
(quote x))'
; → (Val 3)
Attendu : 3. Obtenu : 3. ✓
La bibliothèque marche.
$ ./let '(load mathematica.lisp)'
'(deriv-simp
(quote (Mult (Var x)
(Var x)))
(quote x))'
; → (Add (Var x) (Var x))
Résultat : x + x (non simplifié).
Le principe est là, la perfection viendra.
x + x en 2x. Notre version (1 après-midi, 0 €)
laisse x + x. Même principe, échelle différente.
Choisissez votre combat.
| Fonction | Paramètres | Retourne | Exemple |
|---|---|---|---|
addvec | a b | a + b | (addvec '(1 2) '(3 4)) → (4 6) |
subvec | a b | a − b | (subvec '(5 7) '(4 5)) → (1 2) |
mulvec | a b | a × b (elem) | (mulvec '(2 3) '(5 6)) → (10 18) |
scale | s v | s × v | (scale 5 '(1 2 3)) → (5 10 15) |
dot | a b | a · b | (dot '(1 2) '(3 4)) → 11 |
sum | v | Σ v | (sum '(1 2 3)) → 6 |
norm1 | v | ‖v‖₁ | (norm1 '(3 4)) → 7 |
norm | v | ‖v‖₂ | (norm '(3 4)) → 5.0 |
cross | a b | a × b (3D) | (cross '(1 0 0) '(0 1 0)) → (0 0 1) |
Chaque fonction : quelques lignes de LISP pur, zéro C.
| Fonction | Paramètres | Retourne | Exemple |
|---|---|---|---|
addmat | a b | a + b | (addmat '((1 2)(3 4)) '((5 6)(7 8))) → ((6 8) (10 12)) |
submat | a b | a − b | (submat '((5 6)(7 8)) '((4 5)(6 7))) → ((1 1) (1 1)) |
transpose | m | mᵀ | (transpose '((1 2)(3 4))) → ((1 3) (2 4)) |
mulmat | a b | a × b | (mulmat '((1 2)(3 4)) '((1 0)(0 1))) → ((1 2) (3 4)) |
identity | n | Iₙ | (identity 3) → ((1 0 0) (0 1 0) (0 0 1)) |
zeros | n m | 0n×m | (zeros 2 3) → ((0 0 0) (0 0 0)) |
ones | n m | 1n×m | (ones 2 2) → ((1 1) (1 1)) |
$ ./let '(load matlab.lisp)'
'(addvec (quote (1 2 3))
(quote (4 5 6)))'
; → (5 7 9)
$ ./let '(load matlab.lisp)'
'(mulmat (quote ((1 2)
(3 4)))
(quote ((5 6)
(7 8))))'
; → ((19 22) (43 50))
$ ./let '(load matlab.lisp)'
'(identity 3)'
; → ((1 0 0) (0 1 0) (0 0 1))
$ ./let '(load matlab.lisp)'
'(norm (quote (3 4)))'
; → 5.0
Pas de pip install. Pas de package manager.
Juste ./let '(load matlab.lisp)' et c'est parti.
Le génie, c'est 1% d'inspiration, 99% de transpiration.
Vous avez transpiré pendant 6 cours. Aujourd'hui, vous récoltez.