Bien sûr, la question est de savoir si la macro est pratique à utiliser et quelle est sa puissance.
Voyons d'abord comment Lisp est légèrement différent.
La syntaxe Lisp est basée sur les données, pas sur le texte.
Lisp possède une syntaxe à deux niveaux.
A) il y a d'abord la syntaxe des données pour les expressions s
exemples :
(mary called tim to tell him the price of the book)
(sin ( x ) + cos ( x ))
Les expressions s sont des atomes, des listes d'atomes ou des listes.
B) ensuite, il y a la syntaxe du langage Lisp qui s'ajoute aux s-expressions. Toutes les s-expressions ne sont pas des programmes Lisp valides.
(3 + 4)
n'est pas un programme Lisp valide, car Lisp utilise la notation par préfixe.
(+ 3 4)
est un programme Lisp valide. Le premier élément est une fonction - ici la fonction +
.
Les expressions S sont des données
La partie intéressante est maintenant que les expressions s peuvent être lues et que Lisp utilise les structures de données normales (nombres, symboles, listes, chaînes) pour les représenter.
La plupart des autres langages de programmation ne disposent pas d'une représentation primitive pour la source internalisée - autre que les chaînes de caractères.
Notez que les expressions s ne représentent pas ici un AST (Abstract Syntax Tree). Il s'agit plutôt d'un arbre token hiérarchique issu d'une phase de lexer. Un lexeur identifie les éléments lexicaux.
Le code source internalisé permet maintenant de calculer facilement avec du code, car les fonctions habituelles pour manipuler les listes peuvent être appliquées.
Manipulation simple du code avec les fonctions de liste
Regardons le code Lisp invalide :
(3 + 4)
Le programme
(defun convert (code)
(list (second code) (first code) (third code)))
(convert '(3 + 4)) -> (+ 3 4)
a converti une expression infixe en une expression préfixe Lisp valide. Nous pouvons donc l'évaluer.
(eval (convert '(3 + 4))) -> 7
EVAL évalue le code source converti. eval
prend en entrée une expression s, ici une liste (+ 3 4)
.
Comment calculer avec le code ?
Les langages de programmation disposent désormais d'au moins trois choix pour rendre possible le calcul des sources :
-
baser les transformations du code source sur les transformations des chaînes de caractères
-
utilisent une structure de données primitive similaire à celle de Lisp. Une variante plus complexe de ceci est une syntaxe basée sur XML. On pourrait alors transformer des expressions XML. Il existe d'autres formats externes possibles combinés à des données internalisées.
-
utiliser un véritable format de description syntaxique et représenter le code source internalisé comme un arbre syntaxique utilisant des structures de données qui représentent les catégories syntaxiques. -> utiliser un AST.
Pour toutes ces approches, vous trouverez des langages de programmation. Lisp se trouve plus ou moins dans le camp 2. La conséquence : il n'est théoriquement pas vraiment satisfaisant et rend impossible l'analyse statique du code source (si les transformations du code sont basées sur des fonctions Lisp arbitraires). La communauté Lisp se bat avec cela depuis des décennies (voir par exemple la myriade d'approches que la communauté Scheme a essayé). Heureusement, il est relativement facile à utiliser, comparé à certaines des alternatives et assez puissant. La variante 1 est moins élégante. La variante 3 conduit à beaucoup de complexité dans les transformations simples ET complexes. Cela signifie généralement aussi que l'expression a déjà été analysée par rapport à une grammaire de langage spécifique.
Un autre problème est de savoir COMMENT transformer le code. Une approche serait basée sur des règles de transformation (comme dans certaines variantes de macro Scheme). Une autre approche serait un langage de transformation spécial (comme un langage de template qui peut faire des calculs arbitraires). L'approche Lisp consiste à utiliser Lisp lui-même. Cela permet d'écrire des transformations arbitraires en utilisant le langage Lisp complet. En Lisp, il n'y a pas d'étape d'analyse séparée, mais à tout moment, les expressions peuvent être lues, transformées et évaluées - car ces fonctions sont à la disposition de l'utilisateur.
Lisp est en quelque sorte un maximum local de simplicité pour les transformations de code.
Autre syntaxe frontale
Notez également que la fonction read
lit les expressions s en données internes. En Lisp, on peut soit utiliser un autre lecteur pour une syntaxe externe différente ou réutiliser le lecteur intégré de Lisp et le reprogrammer en utilisant le mécanisme de macro lecture - ce mécanisme permet d'étendre ou de modifier la syntaxe des expressions s. Il existe des exemples pour les deux approches permettant de fournir une syntaxe externe différente en Lisp.
Il existe par exemple des variantes de Lisp qui ont une syntaxe plus conventionnelle, où le code est analysé en expressions s.
Pourquoi la syntaxe basée sur les expressions s est-elle populaire parmi les programmeurs Lisp ?
La syntaxe Lisp actuelle est populaire parmi les programmeurs Lisp pour deux raisons :
1) le données est code est données Cette idée permet d'écrire facilement toutes sortes de transformations de code basées sur les données internalisées. Il existe également un chemin relativement direct entre la lecture du code, la manipulation du code et l'impression du code. Les outils de développement habituels peuvent être utilisés.
2) l'éditeur de texte peut être programmé de manière directe pour manipuler les expressions s. Cela rend les transformations de base du code et des données dans l'éditeur relativement faciles.
À l'origine, on pensait que Lisp avait une syntaxe différente, plus conventionnelle. Plusieurs tentatives ont été faites par la suite pour passer à d'autres variantes de syntaxe, mais pour certaines raisons, elles ont échoué ou ont donné naissance à d'autres langages.
1 votes
Je pense qu'une meilleure façon de formuler cette question serait : Est-ce qu'un langage peut avoir les puissantes macros de Lisp sans que le code soit écrit comme un ensemble de structures de données et qu'il offre la même interface uniforme pour manipuler ces structures de données que celle offerte par le code "normal" ?
0 votes
R le fait. Voir cran.r-project.org/doc/manuels/
1 votes
@DNolen Cela implique de montrer que la même chose peut être accomplie par l'utilisation, par exemple, d'espaces blancs ou d'autres moyens de structurer le code.
0 votes
Oui, en particulier les expressions douces sont probablement ce que vous voulez. Jetez un coup d'œil à lisible.sourceforge.net .
0 votes
À proprement parler, vous pourriez endurer un tout autre type de douleur et écrire tout votre code dans des arbres d'expression dynamiques .net... :)
2 votes
J'ai écrit un petit Préprocesseur d'ajout de parenthèses qui transforme la syntaxe basée sur l'indentation en syntaxe lisp que vous pouvez ensuite transmettre à n'importe quel interpréteur lisp. Comme c'est juste un préprocesseur, vous pouvez utiliser toutes les fonctionnalités de lisp sans avoir besoin de percer les yeux de vos lecteurs avec des tonnes de parenthèses ;)
0 votes
Vous pouvez avoir une syntaxe purement infixe, un peu comme APL ou Smalltalk. Par exemple, au lieu de dire (defun f (a b c) (cond ((= a b) 'eq) (> a b 'gt) (T 'lt))), vous pouvez l'écrire comme f = [a b c] -> {a == b, 'eq} cond {a < b, 'lt} : {T : 'gt}. (let ((a 5)) (+ (* a a) 5)) is [[a, 5]] let a * a + 5 où l'ordre des opérations est de gauche à droite. (do (fun1 a b) (fun2 c d) (fun3 a b c)) est a fun1 b ; c fun2 d ; a fun3 b : c, et (fun1 (fun2 (fun3 a b) c) g e)) est a fun3 b fun2 c fun1 g : e.