150 votes

S'il vous plaît expliquer certains des points de Paul Graham sur Lisp

J'ai besoin d'aide pour comprendre certains points de Paul Graham est Ce qui Fait de Lisp Différents.

  1. Un nouveau concept de variables. En Lisp, toutes les variables sont effectivement des pointeurs. Les valeurs sont ce qui ont, pas des variables, et de l'attribution ou de la liaison des variables signifie copie de pointeurs, pas ce qu'ils pointent.

  2. Un type de symbole. Les symboles diffèrent de chaînes que vous pouvez tester l'égalité en comparant un pointeur.

  3. Une notation pour le code à l'aide des arbres de symboles.

  4. L'ensemble de la langue toujours disponible. Il n'y a pas de véritable distinction entre lecture-temps, au moment de la compilation et de l'exécution. Vous pouvez compiler ou exécuter du code lors de la lecture, de lire ou d'exécuter du code lors de la compilation, et de lire ou de compiler le code au moment de l'exécution.

Que faire de ces points de dire? Comment sont-ils différents dans des langages comme le C ou le Java? N'importe quel autre des langues autres que le Lisp de la famille des langues n'ont aucune de ces constructions maintenant?

100voto

Michał Marczyk Points 54179

Matt explication est parfaitement bien, et il prend un tir à une comparaison avec C et Java, je ne vais pas le faire -- mais pour certaines raisons, j'ai vraiment plaisir à discuter de ce sujet de temps en temps, donc voici mon coup à une réponse.

Sur les points (3) et (4):

Les Points (3) et (4) sur votre liste semble le plus intéressant et encore pertinents aujourd'hui.

Pour les comprendre, il est utile d'avoir une image claire de ce qui se passe avec code Lisp-sous la forme d'un flux de caractères saisie par le programmeur -- sur sa façon d'être exécuté. Prenons un exemple concret:

;; a library import for completeness,
;; we won't concern ourselves with it
(require '[clojure.contrib.string :as str])

;; this is the interesting bit:
(println (str/replace-re #"\d+" "FOO" "a123b4c56"))

Cet extrait de Clojure code imprime aFOObFOOcFOO. Notez que Clojure sans doute ne satisfait pas pleinement le quatrième point sur votre liste, car lisez-le temps n'est pas vraiment s'ouvrir à l'utilisateur le code; je vais discuter de ce que cela signifierait pour que cela soit autrement, si.

Alors, admettons que nous avons ce code dans un fichier quelque part et nous demandons à Clojure pour l'exécuter. Aussi, supposons (pour des raisons de simplicité) que nous avons passé la bibliothèque d'importation. Le morceau intéressant commence à (println et se termine à l' ) loin vers la droite. C'est lexed / analysée comme on pouvait s'y attendre, mais déjà un point important se pose: le résultat n'est pas spécial à compilateur spécifique AST représentation, c'est juste un régulier Clojure / Lisp structure de données, à savoir une liste imbriquée contenant un tas de symboles, de cordes et de -- dans ce cas, un seul compilé regex modèle de l'objet correspondant à l' #"\d+" littérale (en savoir plus sur ce ci-dessous). Certains Lisps ajouter leur propre peu de rebondissements pour ce processus, mais Paul Graham a été essentiellement se référant à la Common Lisp. Sur les points pertinents à votre question, Clojure est similaire à la CL.

L'ensemble de la langue au moment de la compilation:

Après ce point, tout le compilateur traite (ce serait également vrai pour un interpréteur Lisp; Clojure code arrive toujours à être compilé) est Lisp structures de données programmeurs Lisp sont utilisés pour la manipulation. À ce stade, une merveilleuse possibilité devient évident: pourquoi ne pas permettre Lisp les programmeurs d'écrire des fonctions Lisp qui manipulent Lisp données représentant programmes Lisp et de sortie des données transformées représentant transformé programmes, pour être utilisé à la place des originaux? En d'autres mots, pourquoi ne pas autoriser les programmeurs Lisp pour inscrire leurs fonctions en tant que compilateur plugins de toutes sortes, appelé macros en Lisp? Et, en effet, que tout bon système Lisp a cette capacité.

Ainsi, les macros sont régulièrement fonctions Lisp d'exploitation sur le programme de la représentation au moment de la compilation, avant la fin de la phase de compilation lorsque l'objet même code est émis. Depuis il n'y a pas de limites sur les types de code macros sont autorisées à s'exécuter (en particulier, le code qui ils courent est souvent lui-même écrit avec de la libre utilisation de la fonction macro), on peut dire que "toute langue est disponible au moment de la compilation".

L'ensemble de la langue au temps de lecture:

Revenons à qui #"\d+" regex littérale. Comme mentionné ci-dessus, cela se transforme en un véritable compilé modèle de l'objet à lire l'heure, avant le compilateur entend la première mention du nouveau code, être préparé pour la compilation. Comment est-ce possible?

Eh bien, la façon Clojure est actuellement mis en œuvre, la situation est quelque peu différente de ce que Paul Graham avait à l'esprit, même si tout est possible avec un astucieux hack. En Common Lisp, l'histoire serait un peu plus propre sur le plan conceptuel. Les bases sont cependant similaires: le Lecteur Lisp est une machine d'état qui, en plus d'effectuer la transition de l'état et, éventuellement, de déclarer si elle a atteint un "état acceptant", crache Lisp structures de données les personnages représentent. Ainsi, les personnages 123 devenir le nombre 123 etc. Le point important est maintenant: cette machine d'état peut être modifié par le code de l'utilisateur. (Comme indiqué plus haut, c'est tout à fait vrai dans la CL; pour Clojure, un hack (déconseillé & pas utilisé dans la pratique) est nécessaire. Mais je m'égare, c'est PG de l'article, je suis censé être en élaborant, donc...)

Donc, si vous êtes un Common Lisp programmeur et que vous aimez l'idée de Clojure-style vecteur littéraux, il vous suffit de brancher dans le lecteur d'une fonction à réagir de manière appropriée à certains séquence de caractères -- [ ou #[ éventuellement -- et le considérer comme le début d'un vecteur littérale se terminant à la correspondance ]. Une telle fonction est appelée un lecteur de macro et une macro, il peut exécuter n'importe quelle sorte de code Lisp, y compris le code qui a été écrit avec funky notation activé précédemment enregistré lecteur de macros. Donc, il y a toute la langue à lire de temps pour vous.

Conclusion:

En fait, ce qui a été fait jusqu'à présent est que l'on peut exécuter régulièrement des fonctions Lisp à lire l'heure ou le moment de la compilation; la seule étape que l'on doit prendre à partir de là pour comprendre comment la lecture et de la compilation sont eux-mêmes possible à lire, de compilation ou de l'exécution, c'est réaliser que la lecture et de la compilation sont elles-mêmes réalisées par des fonctions Lisp. Vous pouvez les appeler read ou eval à tout moment à lire en Lisp de données de flux de caractères ou de compiler & exécuter du code Lisp, respectivement. C'est l'ensemble de la langue-là, tout le temps.

Notez comment le fait que Lisp, satisfait point (3) à partir de votre liste est essentiel à la façon dont elle parvient à satisfaire le point (4) -- la saveur particulière de macros fournies par Lisp s'appuie fortement sur le code représenté par la régularité de Lisp de données, ce qui est quelque chose activé par (3). D'ailleurs, seulement "l'arbre-ish" aspect du code qui est vraiment essentiel, ici, vous pourrait avoir un Lisp écrite à l'aide de XML.

68voto

Rainer Joswig Points 62532

1) Un nouveau concept de variables. En Lisp, toutes les variables sont effectivement des pointeurs. Les valeurs sont ce qui ont, pas des variables, et de l'attribution ou de la liaison des variables signifie copie de pointeurs, pas ce qu'ils pointent.

(defun print-twice (it)
  (print it)
  (print it))

"elle" est une variable. Il peut être lié à n'IMPORTE quelle valeur. Il n'y a pas de restriction et aucun type associé à la variable. Si vous appelez la fonction, l'argument ne doivent pas être copiés. La variable est similaire à un pointeur. Il a une façon d'accéder à la valeur qui est lié à la variable. Il n'y a pas besoin de réserver de la mémoire. On peut passer n'importe quel objet de données lorsque nous appelons la fonction: toute taille et de tout type.

Les objets de données ont un "type" et tous les objets de données peut être interrogée pour son "type".

(type-of "abc")  -> STRING

2) Un type de symbole. Les symboles diffèrent de chaînes que vous pouvez tester l'égalité en comparant un pointeur.

Un symbole est un objet de données avec un nom. Habituellement, le nom peut être utilisé pour trouver l'objet:

|This is a Symbol|
this-is-also-a-symbol

(find-symbol "SIN")   ->  SIN

Puisque les symboles sont de véritables objets de données, nous pouvons tester si ils sont le même objet:

(eq 'sin 'cos) -> NIL
(eq 'sin 'sin) -> T

Cela nous permet par exemple d'écrire une phrase avec des symboles:

(defvar *sentence* '(mary called tom to tell him the price of the book))

Maintenant, nous pouvons compter le nombre de LA dans la phrase:

(count 'the *sentence*) ->  2

En Common Lisp symboles n'ont pas seulement un nom, mais ils peuvent également avoir une valeur, une fonction, une liste de propriétés et d'un package. Donc, les symboles peuvent être utilisés pour le nom des variables ou des fonctions. La liste des propriétés est généralement utilisé pour ajouter des méta-données de symboles.

3) Une notation pour le code à l'aide des arbres de symboles.

Lisp utilise ses structures de base de données pour représenter code.

La liste (* 3 2) peut être à la fois les données et le code:

(eval '(* 3 (+ 2 5))) -> 21

(length '(* 3 (+ 2 5))) -> 3

L'arbre:

CL-USER 8 > (sdraw '(* 3 (+ 2 5)))

[*|*]--->[*|*]--->[*|*]--->NIL
 |        |        |
 v        v        v
 *        3       [*|*]--->[*|*]--->[*|*]--->NIL
                   |        |        |
                   v        v        v
                   +        2        5

4) l'ensemble de La langue toujours disponible. Il n'y a pas de véritable distinction entre lecture-temps, au moment de la compilation et de l'exécution. Vous pouvez compiler ou exécuter du code lors de la lecture, de lire ou d'exécuter du code lors de la compilation, et de lire ou de compiler le code au moment de l'exécution.

Lisp fournit les fonctions de lecture pour LIRE les données et le code à partir du texte, de la CHARGE de charger du code, EVAL pour évaluer code, la COMPILATION pour compiler le code et d'IMPRIMER des données d'écriture de code et de texte.

Ces fonctions sont toujours disponibles. Ils ne vont pas loin. Ils peuvent être partie intégrante de tout programme. Cela signifie que tout programme peut lire, de la charge, eval ou d'impression de code - toujours.

Comment sont-ils différents dans des langages comme le C ou le Java?

Ces langues ne pas fournir les symboles, code de données ou de l'exécution de l'évaluation des données sous forme de code. Les objets de données dans C sont généralement non typée.

N'importe quel autre des langues autres que le LISP de la famille des langues n'ont aucune de ces constructions maintenant?

Beaucoup de langues ont certaines de ces capacités.

La différence:

En Lisp ces fonctionnalités sont conçues dans la langue de sorte qu'ils sont faciles à utiliser.

35voto

Matt Curtis Points 12454

Pour les points (1) et (2), il est question du passé. Java variables sont à peu près la même, ce qui est pourquoi vous avez besoin de les appeler .equals() pour comparer les valeurs.

(3) est en train de parler sur les S-expressions. Lisp les programmes sont écrits dans cette syntaxe, qui offre beaucoup d'avantages par rapport ad-hoc de la syntaxe, comme Java et C, telles que la capture de motifs répétés dans les macros dans un loin le moyen le plus propre que C des macros ou des modèles C++, et la manipulation de code avec la même base de la liste des opérations que vous utilisez pour les données.

(4) la prise en C par exemple: la langue est vraiment deux sous langues: des trucs comme si() et(), et le préprocesseur. Vous utilisez le préprocesseur pour éviter de devoir répéter vous-même tout le temps, ou pour passer le code avec #si/#ifdef. Mais les deux langues sont tout à fait distinctes, et vous ne pouvez pas utiliser while() au moment de la compilation comme vous pouvez le #si.

C++ cela rend encore pire avec des modèles. Découvrez quelques références sur le modèle de la métaprogrammation, qui fournit un moyen de génération de code au moment de la compilation, et il est extrêmement difficile pour des non-experts pour envelopper leurs têtes. En outre, il est vraiment un tas de hacks et astuces à l'aide de modèles et macros que le compilateur ne peut pas fournir de soutien de première classe pour - si vous faites une simple erreur de syntaxe, le compilateur est pas en mesure de vous donner un message d'erreur clair.

Eh bien, avec Lisp, vous avez tout cela dans une seule langue. Vous utilisez les mêmes trucs pour générer le code au moment de l'exécution que vous apprenez dans votre premier jour. Ce n'est pas pour suggérer la métaprogrammation est trivial, mais il est certainement plus simple avec la première classe de la langue et de la prise en charge du compilateur.

-4voto

CyberFonic Points 2218

Les Points (1) et (2) ou encore Python. En prenant un exemple simple "a = str(82.4)" l'interprète crée d'abord un flottant objet avec la valeur de 82,4. Ensuite, il appelle une chaîne constructeur qui renvoie une chaîne de valeur "de 82,4'. Le " a " sur le côté gauche est simplement une étiquette pour objet de type string. L'original flottant point objet a été ordures collectées, car il n'y a pas plus de références.

Dans le Schéma, tout est traité comme un objet d'une manière similaire. Je ne suis pas sûr de Common Lisp. Je voudrais essayer d'éviter de penser en termes de C/C++ concepts. Ils ont ralenti m'en a des tas, lorsque je cherchais à obtenir ma tête autour de la belle simplicité de Lisps.

-5voto

jcc333 Points 171

Python est en fait presque assez près pour LISP pour être un dialecte. À certains égards, je pense que c'est assez proche de ce que McCarthy envisagées. Il a toujours pour but de mettre en œuvre une syntaxe complète, la seule raison pour laquelle il n'a jamais été parce que LISPers aimé de codage dans les s-expressions de mieux. Essayez de lire certains de Paul Graham essais (http://www.paulgraham.com/icad.html si vous voulez analyser les LISPer sur Python. Pas nécessairement le véritable, juste une pensée. Ou consulter cette page pour avoir une idée de comment ils se rapportent: http://norvig.com/python-lisp.html

edit: Aaronc est en fait totalement droite. Mes excuses pour cela.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X