Ceci est un article supplémentaire à Introduction à Lisp pour Pythonista (traduction japonaise du tutoriel Hy). Dans Tutoriel Hy, la plupart du contenu était ** "Ce que vous écrivez en Python, écrivez ceci en Hy" **, mais cela aussi Certains endroits ne sont pas suffisants pour une utilisation pratique. Dans cet article, je voudrais fournir une explication supplémentaire des caractéristiques uniques de Hy, en d'autres termes, les caractéristiques de Hy ** et les caractéristiques uniques de Hy ** que Python n'a pas. Si vous lisez ceci, je pense qu'il suffit d'utiliser Hy au lieu de Python pour le moment.
J'ai trouvé les bons articles suivants autres que Qiita, alors veuillez vous y référer également. (Article précédent et couverture Moro, c'est plus utile ...)
Il prend en charge les types similaires à Python. Cependant, il existe quelques différences entre les séries Python 2.x et 3.x. Le comportement est le même, mais le type est différent.
Il est long
en Python 2.x et ʻint` en série 3.x.
Entier en Python2
=> (type 2)
<type 'long'>
Entier en Python 3
=> (type 2)
<class 'int'>
En Python 2.x, c'est ʻunicode, et dans la série 3.x, c'est
str`.
Chaîne en Python2
=> (type "a")
<type 'unicode'>
Chaîne en Python 3
=> (type "a")
<class 'str'>
À propos, la chaîne de caractères est placée entre guillemets doubles ("
). Les guillemets (` ``) ne sont pas autorisés. De plus, contrairement à Python, les sauts au milieu sont autorisés.
nil
Hy est Lisp, donc je ne pense pas que cela commencera sans rien. Cependant, Hy est également Python, il est donc plutôt compatible Python en termes de types. Par conséquent ** il n'y a pas de zéro **.
Utilisez une liste vide ([]
ou ()
), un taple vide ((,)
), False
, None
, etc., selon le contexte. Si vous êtes un Pythonista qui n'est pas Lisper, vous pouvez continuer comme avant.
Comme Clojure, contrairement aux autres Lisps, ** il y a une distinction entre parenthèses **.
(...)
Représente une liste. Cependant, comme il s'agit d'un concept légèrement différent de la liste
de Python, nous l'appellerons * liste Lisp * par souci de distinction.
Dans la liste * Lisp *, le premier élément est une fonction et les éléments restants sont appliqués en tant qu'arguments.
Par conséquent, (function value1 value2 ...)
est équivalent à function (* [value1, value2 ...])
en Python.
Liste de Lisp
=> (+ 1 2 3)
6
=> (1 2 3)
Traceback (most recent call last):
File "/usr/local/lib/python3.5/dist-packages/hy/cmdline.py", line 102, in runsource
ast_callback)
File "/usr/local/lib/python3.5/dist-packages/hy/importer.py", line 176, in hy_eval
return eval(ast_compile(expr, "<eval>", "eval"), namespace)
File "<eval>", line 1, in <module>
TypeError: 'int' object is not callable
Dans le deuxième exemple, nous avons évalué «1 (* [2, 3])» en Python, et bien sûr «1» n'est pas «appelable», donc une erreur se produit. Utilisez la citation (`` '') lorsque vous ne souhaitez pas évaluer en tant que fonction.
Liste Lisp avec citations
=> '(1 2 3)
(1 2 3)
[...] Représente une soi-disant liste en Python. On l'appelle aussi un vecteur, à la suite de Clojure. Ceci est équivalent à la liste * Lisp * citée ci-dessus.
Liste de Python
=> [1 2 3]
[1, 2, 3]
=> (= [1 2 3] '(1 2 3))
True
{...}
Comme Python, il utilise un dictionnaire (dict
). Disposez les touches et les éléments en alternance.
dict
=> {"a" 1 "b" 2}
{'b': 2, 'a': 1}
La clé peut également être sous la forme «: clé».
dict partie 2
=> {:a 1 :b 2}
{'\ufdd0:a': 1, '\ufdd0:b': 2}
Utilisez get
pour accéder à l'élément.
Accéder aux éléments de dict
=> (get {"a" 1 "b" 2} "a")
1
=> (get {:a 1 :b 2} :a)
1
De plus, dans le cas de la deuxième méthode d'écriture (: key
), l'accès suivant est également possible.
Accéder aux éléments de dict # 2
=> (get {:a 1 :b 2} :a)
1
=> (:a {:a 1 :b 2})
1
Le tableau jusqu'à présent est résumé. Je ne l'ai pas expliqué, mais j'inclus également des tapples dans le tableau.
Expression dans Hy | Représentation Python correspondante |
---|---|
liste(function arg1 arg2 ...) |
Appel de fonctionfunction(*[args1, arg2, ...]) |
Liste citée'(elem1 elem2 ...) |
liste[elem1, elem2, ...] |
Vecteur[elem1 elem2 ...] |
liste[elem1, elem2, ...] |
Taple(, elem1 elem2 ...) |
Taple(elem1, elem2, ..., ) |
Dictionnaire 1{"key1" elem1 "key2" elem2 ...} |
dictionnaire{"key1": elem1, "key2": elem2, ...} |
Dictionnaire partie 2{:key1 elem1 :key2 elem2 ...} |
dictionnaire{"key1": elem1, "key2": elem2, ...} |
En Python, contrairement à C ++ et Java, il n'est pas possible de changer le corps d'une fonction en fonction du type et du nombre d'arguments. Hy a des macros pour y parvenir dans les modules sous hy.contrib
.
hy.contrib.multi.defn
La macro intégrée defn
est étendue pour réaliser le polymorphisme par le nombre d'arguments.
Surcharge numérique par defn
(require [hy.contrib.multi [defn]])
(defn function
([a](+ "a = " (str a)))
([a b](+ "a = " (str a) ", b = " (str b))))
(print (function 3))
;; > a = 3
(print (function 3 4))
;; a = 3, b = 4
Dans l'exemple ci-dessus, le processus bascule entre le cas où le nombre d'arguments est de un et le cas où le nombre d'arguments est de deux. Soyez assuré que vous pouvez l'utiliser tel quel sans aucun problème si vous l'utilisez de la même manière que la normale defn
.
hy.contrib.multi.defmulti, defmethod, default-method Réalise la spécialisation par envoi multiple. C'est une soi-disant multi-méthode.
Multi-méthode par defmulti
(require [hy.contrib.multi [defmulti defmethod default-method]])
(defmulti add [x y](, (type x) (type y)))
(defmethod add (, int str) [x y]
(+ (str x) y))
(defmethod add (, str int) [x y]
(+ x (str y)))
(default-method add [x y]
(+ x y))
(print (add 1 "st"))
;; > 1st
(print (add "FF" 14))
;; > FF14
(print (add 2 4))
;; > 6
(print (add "Hello, " "world!"))
;; > "Hello, world!"
La macro defmulti
définit les éléments utilisés pour les conditions d'argument. Dans ce cas, nous distribuons avec un tapple (, (type x) (type y))
qui prend deux arguments et stocke les types de ces arguments. La macro defmethod
définit les conditions et définit le contenu d'exécution pour chaque condition. De plus, la macro default-method
peut définir ce qu'il faut faire si aucune des conditions n'est remplie. Dans l'exemple ci-dessus, le type est utilisé comme déclencheur, mais tout peut être déclenché.
Par exemple, le code suivant est correct.
Multi-méthode par defmulti Partie 2
(require [hy.contrib.multi [defmulti defmethod default-method]])
(defmulti funtion [&rest args](first args))
(defmethod funtion 1 [&rest args]
(print "the list of arguments starts with 1"))
(defmethod funtion 2 [&rest args]
(print "the list of arguments starts with 2"))
(default-method funtion [&rest args]
(print "the list of arguments starts with something other than 1 and 2"))
(funtion 1 3 4)
;; > the list of arguments starts with 1
(funtion 2 3)
;; > the list of arguments starts with 2
(funtion 4 8 9 0)
;; > the list of arguments starts with something other than 1 and 2
Dans l'exemple ci-dessus, plusieurs arguments sont reçus et le premier élément est distribué.
C'est comme un générateur Python, mais contrairement à un générateur, vous pouvez accéder aux mêmes éléments encore et encore. Bien entendu, le retard est évalué comme le générateur. En bref, c'est comme une liste avec évaluation des délais + mémos. Il peut être défini avec la macro hy.contrib.sequences.defseq
. Par exemple, Fibonacci peut s'écrire comme suit.
Fibonacci avec séquence retardée
(require [hy.contrib.sequences [defseq]])
(import [hy.contrib.sequences [Sequence]])
;;Puisqu'il est utilisé après l'expansion de la macro, il doit être importé.
(defseq fibonacci [n]
(if (<= n 1) n
(+ (get fibonacci (- n 1)) (get fibonacci (- n 2)))))
(print (get fibonacci 100))
;; > 354224848179261915075
Le code ci-dessus se termine en milliseconde grâce à l'évaluation retardée, mais l'écrire normalement avec une fonction récursive prend énormément de temps.
Dans Hy, vous pouvez utiliser la * syntaxe de point de début * pour les appels de méthode. Un sucre de syntaxe qui vous permet d'écrire (object.method args ...)
comme (.method object args ...)
.
Syntaxe du point précédent (appel de méthode)
=> (def a [1 2 3])
=> (.append a 4)
=> a
[1, 2, 3, 4]
Syntaxe du point précédent (accès à l'espace de noms du module)
=> (import ast)
=> (.parse ast "print(\"Hello, world!\")" :mode "eval")
<_ast.Expression object at 0xb6a2daec>
Comme mentionné à la fin du Tutoriel, Hy a une fonction très utile appelée macro de thread qui améliore la lisibilité. Ceci est hérité de Clojure. Il existe plusieurs types, je vais donc les expliquer individuellement.
->, ->>
Vous pouvez écrire (func3 (func2 (func1)))
comme (-> func1 func2 func3)
. Si ->
, il sera chaîné comme premier argument de l'expression suivante, et si - >>
, il sera chaîné comme dernier argument. Un exemple concret est montré.
->Quand->>Exemple
=> (def a 8.)
=> (-> a (/ 4) (- 1)) ;; (- (/ a 4) 1)
1.0
=> (->> a (/ 4) (- 1)) ;; (- 1 (/ 4 a))
0.5
as->
Avec ->
et - >>
, vous ne pouvez passer des arguments qu'au début ou à la fin. Il ne peut pas être manipulé lorsque vous souhaitez le passer au milieu ou lorsque la position à passer change en fonction de la fonction. C'est «as->» qui y joue un rôle actif.
as->Exemple
=> (as-> a it
... (/ it 4)
... (- 1 it))
-1.0
Ici, ʻa reçoit un nom temporaire de ʻit
.
doto
La couleur des cheveux est légèrement différente, mais je vais les présenter tous ensemble. C'est comme la syntaxe With en D, qui simplifie une série d'appels de méthode à un seul objet. Vous pouvez écrire (obj.method1) (obj.method2) ...
comme (do to obj .method1 .method2 ...)
.
exemple doto
=> (doto [2 3](.append 1) .sort)
[1, 2, 3]
Ceci est particulièrement utile lorsque les parenthèses sont profondément imbriquées. En Python, il existe de nombreux cas où vous pouvez écrire des expressions qui ne sont pas assez bonnes. Prenons un exemple de conversion de «« tanaka taro »« en «Taro Tanaka» ».
Taro Tanaka en Python
" ".join(map(str.capitalize, reversed("tanaka taro".split())))
Il est un peu gênant de suivre le flux de traitement. Vous pouvez faire la même chose avec Hy comme suit.
Taro Tanaka à Hy
(->> "tanaka taro" .split reversed (map str.capitalize) (.join " "))
Non seulement cela a l'air très soigné, mais le déroulement du processus est désormais fluide de gauche à droite. En bref, les macros de thread sont comme les chaînes de méthodes de Ruby et l'UFCS en langage D. La lisibilité sera grandement améliorée, alors utilisez-la de manière positive.
Si vous pensez à autre chose, je l’ajouterai. Si vous avez des erreurs, veuillez nous en informer.
Recommended Posts