Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Lispmeetup #53 PythonベースのLisp方言、 Hyのすすめ

665 views

Published on

Lispmeetup #53 PythonベースのLisp方言、 Hyのすすめ

Published in: Technology
  • Be the first to comment

Lispmeetup #53 PythonベースのLisp方言、 Hyのすすめ

  1. 1. PythonベースのLisp方言、 Hyのすすめ Satoshi Imai Twitter: @masatoi0 GitHub: masatoi
  2. 2. LispからPythonを利用する試み ● CLPython – Common LispによるPythonの実装 – 開発停止中 ● Pythonは言語仕様が標準化されていないので最新の実装に 追従し続けなければならない(つらい) ● burgled-batteries – CPythonからCommon Lispへのブリッジ
  3. 3. Hyとは? ● Python上で動くLisp方言 – HyからPythonの機能を使える – PythonからHyの機能を使える ● Clojureに似た構文を持つ ● マクロで拡張可能 ● Hycc: 実行ファイル、共有ライブラリを生成できるコ ンパイラ
  4. 4. なぜHyを使うのか? ● Pythonのライブラリを使いたい ● たまにしかPythonを使わないと構文を忘れる – HyはClojureに近い構文なので連想が効く ● とにかくS式でプログラムを書きたい ● マクロを使いたい! ● デメリットとしては構文解析のオーバーヘッドが若干 ある
  5. 5. インストール ● Githubから最新版を入れる ● Hyccのインストール $ pip install git+https://github.com/hylang/hy.git $ pip install hycc
  6. 6. 主なコマンド ● hy – REPL起動または.hyファイル読み込み ● hyc – Pythonのバイトコードにコンパイルする ● hy2py – HyからPythonへトランスレートする ● hycc – Cython経由で実行ファイル/共有ライブラリを作る
  7. 7. Emacsで使うとき: hy-mode ● package.elから入れられる ● .hyファイルを開いた状態でM-x inferior-lispで REPLが起動する ● C-x C-eで式単位の評価ができたりする ● デバッガ、引数補完とかは無し M-x package-install hy-mode
  8. 8. 構文はClojureに似ている ● 関数定義 ● 変数定義/代入 (defn fact [n] "docstring" (if (= n 0) 1 (* n (fact (- n 1))))) (def x 10) (setv y 20 z (* x y)) (print (.format "x: {0}, y: {1}, z: {2}" x y z)) ;; x: 10, y: 20, z: 200
  9. 9. ● オプショナル引数 ● 可変長引数 (defn optional-arg [pos1 pos2 &optional keyword1 [keyword2 42]] [pos1 pos2 keyword1 keyword2]) (optional-arg 'pos1 'pos2 'key1) ; => ['pos1', 'pos2', 'key1', 42] (optional-arg 'pos1 'pos2 :keyword2 420) ; => ['pos1', 'pos2', None, 420] (defn plus [&rest args] (setv sum 0) (for [i args] (setv sum (+ sum i))) sum) (plus 1 2 3) ; => 6
  10. 10. ● 無名関数はClojureと同様にfn ● PythonはLISP-1(関数と変数で名前空間が共通) – Schemeっぽい関数定義も可能 ((fn [one two three] (print one two three)) 1 2 3) ;; 1 2 3 (def fact2 (fn [n] (if (= n 0) 1 (* n (fact2 (- n 1))))))
  11. 11. loop/recurマクロ ● 再帰呼び出しできるが末尾再帰最適化はしないの で、Clojureと同様にloop/recurマクロが用意されて いる (require [hy.contrib.loop [*]]) (defn fact3 [n] (loop [[cnt 1] [acc 1]] (if (= cnt n) acc (recur (inc cnt) (* acc cnt)))))
  12. 12. その他の制御構造 ● cond ● Pythonのfor (setv val 20) (cond [(> val 30) (print "too big")] [(< val 10) (print "too small")] [True (print "just size!")]) ;; just size! (for [x (range 0 10)] (print x) (if (= (% x 2) 0) (continue)) (print "not even!"))
  13. 13. ● Clojureのスレッドマクロ ● 例外処理 (+ (- (* 2 3) 4) 5) ; => 7 (-> (* 2 3) (- 4) (+ 5)) ; => 7 (+ 5 (- 4 (* 2 3))) ; => 3 (->> (* 2 3) (- 4) (+ 5)) ; => 3 (as-> (* 2 3) x (- x 4) (+ x 5)) ; => 7 (as-> (* 2 3) x (- 4 x) (+ x 5)) ; => 3 (try (/ 2 0) (except [e ZeroDivisionError] (print "Division by zero")) (except [e TypeError] (print "Type Error")) (else (print "no errors")) (finally (print "all done")))
  14. 14. マクロ定義 ● 伝統的マクロ – バッククォートとアンクォート(~、~@)で組み立てる – 変数捕捉の回避にはgensymが使える – 展開結果の確認にはmacroexpandが使える ;; letはlambdaのシンタックスシュガー (let ((one 1) (two 2) (three 3)) (print one two three)) ((fn [one two three] (print one two three)) 1 2 3) (defmacro let [var-pairs &rest body] (setv var-names (list (map first var-pairs)) var-vals (list (map second var-pairs))) `((fn [~@var-names] ~@body) ~@var-vals)) 展開
  15. 15. データ構造 ● リスト '(1 2 3) (type '(1 2 3)) ; <class 'hy.models.expression.HyExpression'> ;; 参照はnth (nth '(1 2 3) 2) ; => 3 ;; map/reduce/filter (list (map inc (range 1 10))) ; => [2, 3, 4, 5, 6, 7, 8, 9, 10] (reduce + (map inc (range 1 10))) ; => 54 (list (filter (fn [x] (= (% x 2) 0)) (range 1 10))) ; => [2, 4, 6, 8]
  16. 16. データ構造 ● ディクショナリ (setv dict {"dog" "bark" "cat" "meow"}) (type dict) ; => <type 'dict'> ;; 参照はgetメソッド (.get dict "dog") ; => 'bark'
  17. 17. importとrequire ● importでHy/Pythonのパッケージを読み込む ● パッケージがHyのマクロを含む場合はマクロ展開の ステップを挟む必要があるのでrequireを使う (import sys ; 特定の関数だけをインポート [os.path [exists isdir :as dir? isfile :as file?]] ; 別名をつけてインポート [numpy :as np]) (require [hy.contrib.loop [*]] [hy.extra.anaphoric [*]])
  18. 18. ● パッケージを特定して呼び出し (import os) (.get os.environ "LC_CTYPE") ; => 'en_US.UTF-8' (os.system "date") ; Thu Jun 29 15:20:38 JST 2017 ; => 0
  19. 19. クラス定義 ● selfの属性に代入することでスロットを作る (defclass FooBar [object] ;; コンストラクタ (defn --init-- [self x] (setv self.x x)) ;; アクセサ (defn get-x [self] self.x)) (setv obj (FooBar 1)) (.get-x obj) ; => 1
  20. 20. クラス定義 ● クラスの継承 (defclass Dog [object] (defn --init-- [self name] (setv self.name name))) (defclass SubDog [Dog] (defn --init-- [self name type] ;; 親クラスの--init--メソッドを呼び出す (.--init-- (super SubDog self) name) (setv self.type type))) (setv sd1 (SubDog "taro" "akita")) (print sd1.name) ; => taro (print sd1.type) ; => akita
  21. 21. NumPyで行列のかけ算をしてみる (import [numpy :as np]) (def arr (np.array [[1 2 3] [4 5 6] [7 8 9]])) arr.ndim ; => 2 arr.size ; => 9 arr.dtype ; => dtype('int64') ;; スカラー倍 (* arr 3) ;; 要素積 (* arr arr) ;; 行列積 (np.dot arr arr)
  22. 22. ● timeモジュールで処理時間を測ってみる (import time [numpy.random :as rand]) ;; 1000×1000の乱数行列をつくる (def bigarr1 (rand.rand 1000 1000)) (def bigarr2 (rand.rand 1000 1000)) ;; 行列積にかかる時間を計測 (do (def start (time.time)) (np.dot bigarr1 bigarr2) (def elapsed-time (- (time.time) start)) (print (+ (.format "Evaluation took:: {0}" elapsed-time) " seconds"))) ; Evaluation took:: 0.07291960716247559 seconds
  23. 23. timeitマクロを定義する (defmacro timeit [&rest body] (setv start (gensym) end (gensym)) `(do (setv ~start (time.time)) ~@body (setv ~end (time.time)) (print (+ (.format "Evaluation took: {0}" (- ~end ~start)) " seconds")))) (timeit (np.dot bigarr1 bigarr2)) ; Evaluation took:: 0.07291960716247559 seconds
  24. 24. Kerasでニューラルネットのモデルを定義してみる (def model (Sequential)) (.add model (Dense :input-dim 784 :units 512)) (.add model (Activation "relu")) (.add model (Dropout 0.2)) (.add model (Dense :units 512)) (.add model (Activation "relu")) (.add model (Dropout 0.2)) (.add model (Dense :units 10)) (.add model (Activation "softmax")) (.compile model :loss "categorical_crossentropy" :optimizer (Adam) :metrics ["accuracy"]) (define-sequential model [(Dense :input-dim 784 :units 512) (Activation "relu") (Dropout 0.2) (Dense :units 512) (Activation "relu") (Dropout 0.2) (Dense :units 10) (Activation "softmax")] {:loss "categorical_crossentropy" :optimizer (Adam) :metrics ["accuracy"]})
  25. 25. PythonからHyを呼ぶ ● Hyで書かれたモジュールを読み込む前にHy自体 をimportしておく import hy import greetings greetings.greet("Foo") # Hello from hy, Foo print(greetings.THIS_WILL_BE_IN_CAPS_AND_UNDERSCORES) # See? (setv *this-will-be-in-caps-and-underscores* "See?") (defn greet [name] (print "hello from hy," name)) greetings.hy
  26. 26. 人気のインフラに乗る ● ESR: Lispで悟りが得られるけど実際に使うことは ないよ – PG: コンピュータに読めればいいんで言語はプログラ マの自由だよ ● そうはいってもライブラリ少ないよ – →人気のインフラに乗るよ ● Clojure(JVM) ● Clojurescript(Javascript) ● Clasp(LLVM) ● Hy(Python)
  27. 27. まとめ ● HyはPython上に作られたLisp方言 ● 構文はほとんどClojure ● Pythonのライブラリを簡単に使えるし、Hyで書いた ものをPythonから使うのも簡単 ● 良くも悪くも薄いトランスレータなのでPythonの知 識は必要 ● いざとなったらマクロで構文を拡張できる安心感!

×