Homoiconic et "sans restriction" auto modifiant le code de + lisp Est vraiment l'autonomie de la modification?

j'admettrai volontiers que ma connaissance du Lisp est extrêmement minime. Cependant, je suis extrêmement intéressé par la langue et j'ai l'intention de commencer à l'apprendre sérieusement dans un avenir proche. Ma compréhension de ces questions est sans doute imparfaite, donc si je dis quelque chose qui est manifestement erroné, s'il vous plaît, commentez et corrigez-moi plutôt que de baisser les votes.

Vraiment Homoiconic et de l'Auto-modifiable langues

je cherche des exemples de programmation langues qui supportent à la fois L'Homoiconicité (le Code a la même représentation que les données) et sans restriction auto-modification (sans restriction signifie que vous pouvez changer chaque aspect de votre code courant, pas simplement émettre un nouveau code ou changer les pointeurs/délégués de fonction.)

il n'y a que trois exemples que j'ai trouvés jusqu'à présent qui correspondent à ce critère:

  1. Code Machine. Homoiconique en ce que tout est un nombre. Sans restriction modifiables en ce qu'il inclut des pointeurs, qui peuvent être utilisés pour manipuler n'importe quelle adresse mémoire indépendamment du fait que cette adresse contient du code ou des données.
  2. Malbolge. Même raisonnement que le code Machine. Chaque instruction se modifie après avoir exécuté
  3. ADN. Pas un langage de programmation, mais quand même intéressant. Il n'est pas auto-modificateur dans le même sens que le code machine est; où les instructions réelles + données sont modifiées en place. Cependant il est auto-répliquant et peut muter / évoluer selon à son état antérieur (avec des effets secondaires tels que les radiations qui la bousillent de temps en temps). C'est juste une façon indirecte de s'auto-modifier de toute façon. En bref, l'ADN peut se modifier, mais il le fait en se reproduisant dans entirity avec la mutations. Une chaîne physique D'ADN est"immuable".

Pourquoi Lisp n'est pas sur cette liste

Lisp n'est pas sur cette liste parce qu'il me semble que Lisp est seulement presque Homoiconique, et supporte seulement l'auto-modification restreinte. Vous pouvez faire quelque chose comme

(+ 1 2 3)

qui fera la même chose que

(eval '(+ 1 2 3))

Dans la première version (+ 1 2 3) est du code brut, alors que dans la seconde version il s'agit de données. En présumant la vérité de cette affirmation, on peut soutenir que le Lisp n'est même pas homiconique. Le code a la même représentation que les données en ce sens qu'il s'agit à la fois de listes/arbres/S-expressions. Mais le fait que vous devez marquer explicitement laquelle de ces listes / arbres / S-expressions sont du code et qui sont des données pour moi semble dire que le Lisp n'est pas homiconique après tout. Les représentations sont extrêmement similaires, mais elles diffèrent dans le détail minuscule que vous avez à dire réellement si vous avez affaire avec le code ou les données. Ce n'est pas du tout une mauvaise chose (en fait tout le reste serait de la folie), mais cela met en évidence une différence entre le Lisp et le code machine. Dans le code machine, vous n'avez pas à marquer explicitement les nombres qui sont des instructions, qui sont des pointeurs, et qui sont données. Tout est simplement un nombre jusqu'à ce qu'une interprétation soit réellement nécessaire, à ce moment-là, il pourrait être l'une de ces choses.

C'est un argument encore plus fort contre l'auto-modification sans restriction. Bien sûr, vous pouvez prendre une liste qui représente du code et la manipuler. Par exemple, l'évolution

'(+ 1 2 3)

'(+ 1 4 3)

puis que vous exécutez par eval. Mais quand tu fais ça, tu compiles juste un peu de code et il s'exécute. Vous ne modifiez pas le code existant, vous émettez et lancez du nouveau code. C# peut faire exactement la même chose en utilisant des arbres d'expression, même si dans un format moins commode (qui se produit en raison de C# code ayant une représentation différente de son AST, par opposition à Lisp, qui sa propre AST). Pouvez-vous réellement prendre un fichier source entier et commencer à modifier ce fichier source entier pendant qu'il est en cours d'exécution, avec les modifications apportées au fichier source ayant des effets en temps réel sur le comportement du programme?

a moins qu'il n'y ait un moyen de le faire, le Lisp n'est ni homiconique ni auto-modificateur. (Pour remettre un argument sur les définitions, Lisp n'est pas homoiconique ou auto-modificateur au même degré que le code machine est.)

façons de rendre le Lisp Homoiconique/auto-modifiable sans restriction

je peux voir les 3 façons de faire Lisp homoiconic/auto-modifiable comme machine code.

  1. architecture Non-Von-Neumann. si quelqu'un pouvait inventer une machine hypothétique étonnante où la représentation la plus basse des programmes est un AST qui peut être exécuté directement (aucune compilation supplémentaire n'est nécessaire). Sur une telle machine, un AST représente à la fois des instructions exécutables et des données. Malheureusement, le problème n'aura pas été résolu, car le TSA doit toujours être soit du code, soit des données. La presence d'une fonction eval ne changez pas ce paramètre. En code machine, vous pouvez faire des aller-retours entre le code et les données autant que vous le voulez. Alors qu'avec eval et Lisp une fois que vous avez "évalué" une liste de données en code et l'avez exécutée, il n'y a aucun moyen de récupérer cette liste en tant que données à nouveau. En fait, cette liste est partie pour toujours et a été remplacée par sa valeur. On raterait quelque chose de crucial, qui se trouve être des indices.
  2. Liste Des Étiquettes. S'il était exigé que chaque liste ait aussi une étiquette unique, il serait possible de faire de l'auto-modification indirecte en exécutant des fonctions contre une liste avec une étiquette donnée. Combiné avec des continuations cela permettrait finalement de modifier le code dans le même sens que le code machine l'a. Les étiquettes sont équivalentes aux adresses mémoire de code machine. Par exemple, considérons un programme Lisp où le noeud supérieur de L'AST a l'étiquette "main". À l'intérieur de main vous pourriez alors exécuter une fonction qui prend un label, un entier, un atome, et des copies l'atome de la Liste avec une étiquette qui correspond à celui fourni à la fonction, à l'index spécifié par l'Entier. Ensuite, il suffit d'appeler avec la suite actuelle sur main. Voilà, Code Auto-modificateur.
  3. Lisp Macros. Je n'ai pas pris le temps de comprendre les macros Lisp, et ils peuvent en fait faire exactement ce à quoi je pense.

Point 1. combiné avec 2. produirait un Lisp totalement auto-modificateur. Pourvu que la Machine magique Lisp décrit pourrait être produit. 2. seul un Lisp auto-modifiable pourrait être produit, mais la mise en œuvre sur une architecture Von Neumann pourrait être extrêmement inefficace.

Les Questions

  1. y a-t-il des langues autres que le code machine, l'ADN et le malbolge qui peuvent faire l'auto-modification totale et qui sont homoiconiques?
  2. (PAS la peine de répondre si vous avez un tl;dr à le texte ci-dessus). Est lisp vraiment homoiconic + auto modifier? Si vous le dites, pouvez-vous citer exactement où dans mon argument je me suis égaré?

Annexe

langues avec auto-modification sans restriction mais sans homiconicité

  1. Assemblée. Le code utilise des mots par opposition à des nombres, donc perd l'homiconicité, mais il a encore des pointeurs, qui conserve le contrôle total sur la mémoire et permet l'auto-modification sans restriction.
  2. tout langage qui utilise des pointeurs bruts. Pour exemple C / C++ / Objectif C. même argument que Assembly
  3. JIT langages qui incluent des pointeurs virtuels. Par exemple, c#/.net tourne dans un contexte dangereux. Même argument que L'Assemblée.

autres concepts et langages qui peuvent être pertinents / intéressants: Lisp, Ruby, Snobol, de Suite et il est temps de compilation métaprogrammation, Smalltalk et c'est la réflexion, le lambda calcul non typé avec c'est bien que tout est une fonction (le type Qui implique que en supposant que nous pourrions inventer une machine qui exécute lambda calculus directement, lambda calculus serait homoiconique et Von Neumann code machine ne serait pas lorsque exécuté sur ladite machine. [Et le théorème de Godels serait exécutable. Haha, pensée effrayante :P])

25
demandé sur TheIronKnuckle 2011-12-13 18:12:32

4 réponses

Dans la première version (+ 1 2 3) est du code brut, alors que dans la seconde version il s'agit de données. En présumant la vérité de cette affirmation, on peut soutenir que le Lisp n'est même pas homiconique. Le code a la même représentation que les données en ce sens qu'il s'agit à la fois de listes/arbres/S-expressions. Mais le fait que vous devez marquer explicitement laquelle de ces listes/arbres/S-expressions sont du code et qui sont des données à moi semble dire que le Lisp N'est pas homiconique après tout.

Ce n'est pas vrai. Dans la première version, la liste (+ 1 2 3), qui est data, est envoyé à l'interpréteur pour être exécuté, i.e. pour être interprété comme code. Le fait que vous devez marquer S-expressions comme étant du code ou des données dans un contexte spécifique ne rend pas le Lisp Non homoiconique.

le point d'homoiconicité est que tous les programmes sont des données, pas que toutes les données sont des programmes, donc il y a toujours une différence entre les deux. In Lisp,(1 2 3) est une liste valide, mais pas d'un programme valide depuis un entier n'est pas une fonction.

[si nous regardons L'autre grand langage de programmation homoiconique, Prolog, alors nous voyons le même phénomène: nous pouvons construire une structure de données foo(X, 1, bar), mais sans une définition de foo, nous ne pouvons pas l'exécuter. En outre, les variables ne peuvent pas être les noms des prédicats ou des faits, donc X. n'est jamais un programme valide.]

le Lisp s'auto-modifie dans une large mesure. E. g., voici comment changer la définition d'une fonction:

[1]> (defun foo (x) (+ x 1))
FOO
[2]> (defun bar (x) (+ x 2))
BAR
[3]> (setf (symbol-function 'foo) #'bar)
#<FUNCTION BAR (X) (DECLARE (SYSTEM::IN-DEFUN BAR)) (BLOCK BAR (+ X 2))>
[4]> (foo 3)
5

Explication:[1], nous avons défini la fonction foo pour être la fonction add-1. [2], nous avons défini bar pour être la fonction add-2. [3], nous avons reset foo à la fonction add-2. [4], nous voyons que nous avons réussi à modifié foo.

19
répondu Fred Foo 2011-12-13 14:51:07

Lisp est une famille de langages de programmation. Les membres de cette famille ont des capacités et des techniques de mise en œuvre très différentes. Il y a deux interprétations différentes de ceci:

  • Lisp est une famille de langues qui partagent un ensemble de fonctionnalités se chevauchant. Cela inclut tout depuis le premier Lisp, Maclisp, Scheme, Logo, Lisp commun, Clojure et des centaines de langues différentes et leurs implémentations.

  • Lisp a également une branche principale de langues qui ont aussi la plupart du temps' Lisp ' dans leur nom: Lisp, MacLisp, Common Lisp, Emacs Lisp, ...

beaucoup de recherche a été investie au fil du temps pour améliorer les langages (le rendre plus "fonctionnel" ou le rendre plus orienté objet, ou les deux) et pour améliorer les techniques de mise en œuvre.

Lisp commun par exemple soutient la compilation, Diverses optimisations, et plus pour permettre aux développeurs de l'utiliser dans de grands projets où certains il faut trouver un équilibre entre la souplesse et les caractéristiques. Une fonction compilée est alors du code machine et non plus une structure de données faite de listes, de symboles, de chaînes et de nombres.

Common Lisp permet aux implémentations de créer du code statique. Dans le même temps, il laisse place à des modifications d'exécution contrôlées (par exemple en utilisant un compilateur d'exécution, en chargeant le code, en évaluant le code, en remplaçant le code, etc.)...).

OTOH si vous avez une implémentation Lisp avec un interpréteur et en outre, L'interpréteur peut utiliser les structures de données Lisp pour représenter la source, alors vous pouvez modifier les programmes en cours d'exécution par exemple en changeant la structure de la liste. Il existe des implémentations de dialectes de Lisp qui permettent cela. Une chose typique est l'utilisation de macros, qui peut être calculée à l'exécution, par un tel système. Certains autres Lisps ont des Fexprs, qui sont un mécanisme similaire (mais qui ne peuvent généralement pas être compilés efficacement).

dans une application Lisp commune (par exemple, un système de CAO écrit dans le Lisp) là où une grande partie de l'information source a été supprimée par un outil de livraison, cela ne serait pas possible. On aurait un seul exécutable de code machine, qui a une grande partie de la flexibilité d'exécution supprimée.

L'Homoiconicité n'est pas non plus un concept très bien défini. Pour Lisp j'aime plus que nous disons que le code source peut être transformé en données, parce que le code source utilise la représentation externe de s-expressions, qui est une sérialisation de données format. Mais toutes les expressions-s ne sont pas un programme Lisp valide. De plus, une représentation interne du code source en tant que données Lisp n'est pas "iconique" de quelque manière que ce soit. Le Lisp a le code source comme s-expressions externes et après avoir lu le code source, il a une représentation interne comme données Lisp. Lisez-le, imprimez-le et EVAL l'évalue.

il y a aussi d'autres approches dans le Lisp pour fournir l'accès au Lisp qui exécute votre programme et au programme:

  • le protocole meta-object dans CLOS (le système commun D'objets Lisp) est un tel exemple

  • 3Lisp fournit une tour infinie d'interprètes. Il y a un interpréteur Lisp qui exécute votre programme. Cet interpréteur de Lisp est dirigé par un autre interpréteur de Lisp, qui tourne à nouveau dans un autre, et celui-là aussi...

11
répondu Rainer Joswig 2011-12-13 21:17:20

les Macros peuvent éviter de citer, si c'est ce que vous recherchez:

> (defmacro foo (x) (cdr x))
> (foo (+ - 5 2))
3

(+ - 5 2) code ou données? À l'Heure de l'écriture, on dirait des données. Après le temps de macro-expansion, on dirait du code. Et si la définition de foo était ailleurs, il pourrait tout aussi bien être (incorrectement) considéré comme une fonction, auquel cas (+ - 5 2) sera considéré comme un code qui se comporte comme des données qui se comporte comme le code.

2
répondu tsm 2011-12-15 06:48:39

je viens comme un fanboy ici, et je l'ai dit avant, mais lisez Paul Graham Sur Le Langage Lisp si vous voulez en savoir plus sur les Macros de Lisp. C'est important pour permettre des modifications qui seraient autrement irréalisables. En outre, je pense qu'il est important de distinguer ici entre Lisp la famille linguistique, un Lisp donné, et une mise en œuvre Lisp donnée.

je suppose que la question principale que je prends avec votre argument vient dans le premier paragraphe après "Pourquoi Lisp n'est pas sur cette liste.", et a à voir avec le REPL en Lisp. Lorsque vous saisissez exp (+ 1 2 3) dans l'interpréteur, vous appelez en fait la fonction EVAL sur la liste (+ 1 2 3). Le" code brut "que vous décrivez est en fait des" données", étant envoyé à d'autres codes lisp, ce sont juste des données dans un contexte spécifique.

2
répondu jcc333 2012-02-24 18:43:13