[JAVA] Exemple d'implémentation d'un système de traitement LISP simple [Résumé de chaque version linguistique]

Cet article implémente le traitement minimal d'entrée / sortie d'expression S et de liste dans divers langages de programmation, puis [Description primitive de l'interpréteur LISP] de John McCarthy (http://www-formal.stanford.edu/jmc/jmc). /recursive/recursive.html) implémenté par Paul Graham en Common Lisp "McCarthy's Original Lisp" (jmc.lisp) pour chaque langue Il s'agit d'un ensemble de liens et d'explications courantes d'exemples de description qui ont été portés et vérifiés pour leur fonctionnement.

En outre, il existe également un article qui résume un exemple qui implémente uniquement un traitement de liste de base et un simple analyseur de description utilisant plusieurs types de parenthèses, et il peut être incorporé en extrayant ou en modifiant cette description.

Cet article est plus récent et organisé petit à petit, mais il peut y avoir des incohérences avec chaque article lié, ou des descriptions et des explications en double. Nous apprécierions votre compréhension.

Objectif de l'exemple de mise en œuvre

Depuis le début du développement, le langage LISP a été implémenté comme un évaluateur méta-circulaire, dans lequel le système de traitement LISP est décrit par LISP lui-même. Une telle implémentation est possible s'il s'agit d'un système de traitement LISP avec les fonctions minimales, et le mécanisme de l'évaluateur est très simple. En plus de McCarthy's Original Lisp, [SICP 4.1](https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/ De nombreux exemples de description sont disponibles sur le Web, comme book-ZH-26.html).

En faisant référence à ceux-ci, le même système de traitement LISP qui a les propriétés d'un évaluateur hypercirculaire peut être facilement implémenté dans des langages de programmation autres que le système LISP, et ** est idéal pour démarrer avec l'implémentation du système de traitement du langage ** ... Il devrait l'être, mais s'il s'agit d'un système de traitement LISP, il est équipé en standard d'un traitement d'entrée / sortie "S expression" qui définit les phrases et la syntaxe, et d'un traitement de liste de base basé sur l'expression S (car, cdr, cons, eq, eq, L'implémentation d'atome) prend énormément de temps et d'efforts pour chaque langage de développement, ce qui est un seuil.

Par conséquent, ** un simple exemple d'implémentation d'entrée / sortie de type S et de traitement de liste de base est créé séparément dans chaque langage de programmation **, et "McCarthy's Original Lisp" est possible. Le but de chaque exemple d'implémentation est cette fois d'abaisser le seuil initial d'implémentation du système de traitement de langage en portant et en vérifiant l'opération telle qu'elle est.

Vue d'ensemble du système de traitement

Comme illustré dans l'exemple suivant, il n'y a pas de méthode de description de dénomination ou de définition de fonction et un seul groupe d'expressions S est traité. Cependant, comme il s'agit d'une étendue dynamique, une fonction récursive est définie à l'aide d'une expression lambda. Il peut également être utilisé (à la place des étiquettes letrec ou Common Lisp` de Scheme).

(car (cdr '(10 20 30)))
=> 20

((lambda (x) (car (cdr x))) '(abc def ghi))
=> def

((lambda (f x y) (f x (f y '()))) 'cons '10 '20)
=> (10 20)

((lambda (f x y) (f x (f y '())))
 '(lambda (x y) (cons x (cons y '())))
 '10 '20)
=> (10 (20 ()))

((lambda (assoc k v) (assoc k v))
 '(lambda (k v)
    (cond ((eq v '()) nil)
          ((eq (car (car v)) k)
           (car v))
          ('t (assoc k (cdr v)))))
 'Orange
 '((Apple . 120) (Orange . 210) (Lemmon . 180)))
=> (Orange . 210)

Le contenu de la mise en œuvre du corps de l'évaluateur est le suivant.

La configuration de l'implémentation du traitement des entrées / sorties et des listes de type S est la suivante.

Explication de l'évaluateur

Le contenu de traitement de s_eval est le suivant. De plus, contrairement à l'original «jmc.lisp», la fonction «label» est omise.

Le but est d'appliquer la valeur de l'expression lambda après avoir lié l'expression lambda à l'argument d'une autre expression lambda, par exemple.

(s_eval '((lambda (f) (f '(a b c))) '(lambda (x) (cdr x))) '()))
=> (s_eval '(f '(a b c)) '((f (lambda (x) (cdr x))) . ()))
=> (s_eval '((lambda (x) (cdr x)) '(a b c)) '((f (lambda (x) (cdr x))) . ()))
=> (s_eval '(cdr x) '((x (a b c)) (f (lambda (x) (cdr x))) . ()))
=> (cdr (s_eval 'x '((x (a b c)) (f (lambda (x) (cdr x))) . ())))
=> (cdr '(a b c))
=> (b c)

Est-ce un processus qui est exécuté comme ça? En donnant un nom à l'expression lambda dans la variable d'environnement, vous pouvez définir un processus récursif qui s'appelle lui-même par ce nom.

Il n'y a qu'une seule variable d'environnement, même si l'argument tourne. Par conséquent, cela devient une portée dynamique, mais cette fois, l'évaluateur considère une expression S avec uniquement une expression lambda comme une erreur (plutôt, elle ne renvoie pas telle quelle sous forme de valeur autre qu'une valeur booléenne et une description entre guillemets), et il s'agit d'une expression lambda. Une expression lambda qui décrit une expression lambda en tant que corps de traitement, c'est-à-dire une expression lambda qui renvoie une expression lambda ne peut pas être traitée. En tant que fonction de fonction d'ordre supérieur, elle équivaut à ce que l'on appelle un objet de seconde classe.

En fait, il n'est pas impossible de renvoyer l'expression lambda telle quelle, comme la valeur booléenne, mais il doit s'agir d'une portée lexicale qui contient la variable d'environnement locale dans l'expression lambda et implémente la fonction de fermeture. , Le problème du conflit de nom (funarg) se produit. Et puisque la valeur de la variable d'environnement locale dans une autre expression lambda ne peut pas être appliquée dans la portée lexicale, pour la définition de traitement récursif, la syntaxe pour ajouter la variable liant directement à la variable d'environnement globale et au point d'immobilité tel que le combinateur Y. Vous aurez besoin d'un combinateur.

Remarques

Informations complémentaires sur l'article

Interviewer "Le CV dit que vous pouvez parler la langue ◯◯. Dans quelle mesure l'avez-vous bien apprise?" Vous "Eh bien, est-ce juste une simple implémentation d'un système de traitement du langage?" Interviewer "Quelles sont les spécifications du langage d'implémentation? (S'agit-il d'une copie de programme informatique utilisant la bibliothèque d'analyseurs?)" Vous "Eh bien, au moins pouvez-vous définir une procédure récursive?" Intervieweur "!?" Vous "Non, c'était un peu difficile car je n'ai pas utilisé la bibliothèque pour l'analyse de phrases et la génération d'arbres de syntaxe abstraite." Intervieweur "!?"

C'est juste pour ~~ de bien faire les choses ~~. Ce n'est pas un mensonge. Hein, je suis? Je suis en mesure de dire: "Il est naturel de pouvoir le faire dans n'importe quelle langue, cela prend plutôt trop de temps et de perte, et il n'y a pas de vérification des erreurs."

sample.scm


(car (cdr '(10 20 30)))
=> 20

((lambda (x) (car (cdr x))) '(abc def ghi))
=> def

((lambda (f x y) (f x (f y '()))) cons '10 '20)    ;Le nom de la fonction passé en argument n'a pas besoin d'être entre guillemets
=> (10 20)

((lambda (f x y) (f x (f y '())))
 (lambda (x y) (cons x (cons y '())))    ;L'expression lambda passée en argument n'a pas non plus besoin d'être entre guillemets
 '10 '20)
=> (10 (20 ()))

(letrec ((assoc_ (lambda (k v)
                      (cond ((eq? v '()) '())
                            ((eq? (car (car v)) k)
                             (car v))
                            (else (assoc_ k (cdr v)))))))
  (assoc_ 'Orange
          '((Apple . 120) (Orange . 210) (Lemmon . 180))))
=> (Orange . 210)

sample.lsp


(car (cdr '(10 20 30)))
=> 20

((lambda (x) (car (cdr x))) '(abc def ghi))
=> DEF    ;L'affichage de l'alphabet en tant que symbole est entièrement en majuscules

((lambda (f x y) (funcall f x (funcall f y '()))) 'cons '10 '20)    ;La fonction passée en argument est exécutée en utilisant funcall
=> (10 20)

((lambda (f x y) (funcall f x (funcall f y '())))    ;L'expression lambda passée en argument est exécutée à l'aide de funcall
 (lambda (x y) (cons x (cons y '())))    ;les expressions lambda n'ont pas besoin d'être entre guillemets
 '10 '20)
=> (10 (20 NIL))

(labels ((assoc_ (k v)
           (cond ((eq v '()) '())
                 ((eq (car (car v)) k)
                  (car v))
                 (t (assoc_ k (cdr v))))))
  (assoc_ 'Orange
          '((Apple . 120) (Orange . 210) (Lemmon . 180))))
=> (ORANGE . 210)

Journal des modifications

Recommended Posts

Exemple d'implémentation d'un système de traitement LISP simple [Résumé de chaque version linguistique]
Exemple d'implémentation d'un système de traitement LISP simple (version Java)
Exemple d'implémentation d'un système de traitement LISP simple (version Ruby)
Résumé des bases du langage Java
Résumé du traitement des erreurs Java
Implémentation du traitement asynchrone dans Tomcat
[Kotlin] Un exemple de traitement utilisant Enum