Your SlideShare is downloading. ×
0
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Lisp tutorial for Pythonista : Day 2

1,657

Published on

The Basics

The Basics

Published in: Technology
1 Comment
0 Likes
Statistics
Notes
  • 実用言語という側面でやってるので、Lisper的には「ダサイ」コーディングとか「それ違うだろ!」とか色々出てきます。基本的に「普通な感じに使える速いPython」としてLispを見てて、深いところには興味ないので、そこんとこよろしく。
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Be the first to like this

No Downloads
Views
Total Views
1,657
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
1
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Lisp tutorial for Pythonista.Day #2 : The Basics. Ransui Iso Strategic Technology Group, X-Listing Co, Ltd.
  • 2. 今日はプログラムを書くための 基礎部分を勉強します
  • 3. 前回の宿題は OK ですか?いちおう Emacs + SLIME が動く前提です
  • 4. なにはともあれまずは REPL Read, Eval, Print Loop の略語Python なら「インタラクティブシェル」
  • 5. Emacs 起動して M-x slimeMETA キーの設定無い人は [ESC] 押して [x]
  • 6. ちゃんと起動するとこうなるはず
  • 7. ぱいそん>>> 1 + 2>>> x = 1>>> y = 2>>> z = x * y>>> zりすぷCL­USER> (+ 1 2)CL­USER> (setf x 1)CL­USER> (setf y 1)CL­USER> (setf z (+ x y))CL­USER> z
  • 8. なんか警告出てます!; in: LAMBDA NIL;     (SETF X 1); ==>;   (SETQ X 1); ; caught WARNING:;   undefined variable: X
  • 9. 変数は宣言してから使うのが基本 ルーズな Python とは大違いだよ!管理大好きスーツ族への強力なアピールポイントだ! 警告ガン無視でも、まあ問題は無いんだが気持ち悪いので宣言しとけ。
  • 10. 変数宣言の前に名前空間の話Python と似てる部分もあるし違う部分もある
  • 11. Common Lisp の名前空間● 大域は package という単位が基本になっている ● 概念的には Python のモジュールに近い ● Python のモジュールより厳密に定義するのでお手軽じゃない● 何も指定しない時は CL­USER がデフォルト ● cl パッケージに Common Lisp の基本機能が入っている ● イメージとしては cl­user.py の最初で from cl import * してある感覚。● もちろんローカル変数とかもちゃんとあるよ ● 関数引数とか、 let フォームとか色々ある ● レキシカルスコープなので、基本部分は Python 感覚で OK 。
  • 12. パッケージ変数の宣言● defvar と defparameter ● どちらもパッケージ変数を宣言する ● 宣言するときには初期値を与える● defvar ● 再初期化されない● defparameter ● 再初期化される パッケージ変数という用語は一般的じゃない。 Python 風な名前で呼んで みてるだけ。ほんとは「スペシャル変数」と言う。実は深い意味もあるん だけど、今は無視しちゃう。
  • 13. 再初期化される?されない?● 実験してみりゃ一目瞭然CL­USER> (defvar *foo* nil)*FOO*CL­USER> (defparameter *bar* nil)*BAR*CL­USER> (setf *foo* 1)1CL­USER> (setf *bar* 2)2CL­USER> (defvar *foo* nil)*FOO*CL­USER> *foo*1CL­USER> (defparameter *bar* nil)*BAR*CL­USER> *bar*NIL パッケージが再読み込みされた時とかに挙動が変わるよ!
  • 14. CL のパッケージは取扱いが面倒なので、今のところは CL-USER 一辺倒でいく ライブラリとか作るようになったら defpackage とか使うよ
  • 15. ソースをファイルに書いて実行する● C­x 5 2 で新フレームを開いて● 新しいフレームで C­x C­f hello.lisp とかし て新規バッファを開く● コード書く(defun hello (name)  (format t "Hello ~a~%" name))(hello "World")● C­x C­s でセーブ● C­c C­k で REPL 環境にコンパイルしてロード
  • 16. スクリプトっぽく実行するぱいそんdef hello(name): print("Hello %s" % name)def main(): hello("World")if __name__ == "__main__": main()りすぷ(defun hello (name) (format t "Hello ~a~%" name))(defun main() (hello "World"))(eval­when (:compile­toplevel :load­toplevel :execute) (main) (quit))eval­when は結構使い方が難しい。コンパイル、ロード、実行のタイミングとかはLisp 処理系の動きをイメージしないといけない部分があるからね。
  • 17. コマンドラインから起動する● 実は処理系依存なのよ ● SBCL の場合は下のようにする$ sbcl ­­script hello.lispHello World$
  • 18. お待たせしましたプログラム作成のお時間です
  • 19. すごく教科書的でアレなんですが 「単語数え」 プログラムを書いてみませう
  • 20. 慣れ親しんだ Python だと あー、簡単のために単語は空白文字で区切られてるものとしてます。def main() counter = dict() for line in open(sys.argv[1], "r"): for word in line.strip().split(): index_word = word.lower() if index_word in counter: counter[index_word] += 1 else: counter[index_word] = 1 for (word, num) in counter.items(): print("%20s : %4d" % (word, num))if __name__ == "__main__": main() まぁ、瞬殺なわけです これを Common Lisp で書くとどうなるか
  • 21. 単語数えプログラムを書くために● ファイル入出力 ● ファイルのオープン ● パスの操作● 文字列操作 ● スペースとかのデリミタでのフィールド分解● 数え上げ ● Python で言う所の辞書の使い方● その他 ● コマンドライン引数の扱いとか
  • 22. とりあえず辞書から攻め込む 先週の復習からはじめよう
  • 23. ぱいそんd = dict()d["Hello"] = "World"d["Hello"] → "World"d[1] = 2len(d) → 2for (key, value) in d.items(): print("%s : %s" % (key, value))りすぷ(setf d (make-hash-table :test #equal))(setf (gethash "Hello" d) "World)(gethash "Hello" d) → "World"(setf (gethash 1 d) 2)(hash-table-count d) → 2(maphash #(lambda (key value) (princ (format nil "~a : ~a~%" key value))) d)
  • 24. まぁそのままでもイイんだがもちっと親しみやすくしたいよね!
  • 25. 辞書っぽくアクセスするようにしてみる(define­condition key­error (error)  ((text :initarg :text :reader text)))(defun make­dict ()    (make­hash­table :test #equal))(defun set­item (dict key value)  (setf (gethash key dict) value))(defun get­item (dict key)  (multiple­value­bind (value has­key) (gethash key dict)    (if (not has­key)        (error key­error :text (format nil "KeyError : ~a" key))        value)))(defun has­key (dict key)  (multiple­value­bind (value has­key) (gethash key dict)    (declare (ignore value))    has­key))(defun items (dict)  (let ((result nil))    (maphash #(lambda (key value)                 (setf result (cons (cons key value) result))) dict)    result))
  • 26. なんか、色々と新しいの出た!細かい部分とか技とかは後日のお楽しみとして ポイントになる部分をザックリと解説
  • 27. define-condition● Python で言う所の例外に近い ● 厳密には違うけどイメージとしてはこんなかんじclass KeyError(Exception): def __init__(self, text): Exception.__init__(self, text) KeyError は組み込みの例外だが、これはあくまで例だ。気にするな。● Common Lisp では「コンディション」と言う ● 「例外」と呼ばない点に注意 ● 例外的な事象への対応以外にも色々と使える ● Python の try 〜 except よりもずっと強力
  • 28. eq, eql, equal, equalp● オブジェクトの比較関数だよ ● オブジェクトの同一性と、値としての同値ってのは別腹 ● Python の is と == 演算子の違いに似てる – eq : メモリ上で同一オブジェクトかどうかをチェック – eql : 数値 or 文字の場合は同値か?それ以外は eq する – equal : 値の構造が同一か?内部構造も再帰的にチェック – equalp :equal よりも緩い判定。詳細は CLtL2 参照
  • 29. ハッシュテーブルの生成部分● キーの比較関数を指定して生成してる (make­hash­table :test #equal) ● #equal という表記は (function equal) の略記法 ● 略記法があるってことは、今後もいっぱいでてくる予感 ● ちなみに比較関数指定を省略したときは #eql がデフォルト● equal を指定している訳 ● 今回は文字列をハッシュのキーにする ● 文字列は (eql "Hello" "Hello") → nil なのだ ● ちなみに純粋な文字列比較関数の string= を使ってもいい – その場合、この hash-table のキーはマジで文字列限定になる。
  • 30. setf ってなんだ?● これは違和感ないはず(setf x 10)● で、これは?(setf (gethash key dict) value)● そもそも gethash 関数って(gethash "Hello" dict) → "World" ● みたいに値を取り出す関数なんじゃねーの? ● つーことは、 "Hello" とか、出てきた値になんか代入すんの?
  • 31. setf には値を入れる場所を指定する● ぶっちゃけ、ポインタです ● setf は関数じゃないんです。マクロなんです。つーことは引数は 評価される前の状態で setf に渡されてるということ ● 第一引数は「値を入れる場所」として解釈される dict Hello (gethash "Hello" dict) foo setf マクロと組み合わせてポインタ bar として振る舞う関数には制限がある 詳しくは「 CLtL2 C7.2: 汎変数」を 参照のこと
  • 32. 多値関数● 複数の値を返す関数 ● Python でタプル返すのとはちょっと違う。def foo(x): return ((True if x % 2 else False), x + 1)(is_odd, next) = foo(3) # OKis_odd = foo(3)         # is_odd は単純にタプルを指すだけ(defun foo(x) (values  (if (= (mod x 2) 1) t nil) (+ x 1)))(multiple­value­bind (is_odd next_x) (foo 3)  ; 多値を受け取るための形式 (cons is_odd next_x))(setf is_odd (foo 3))                         ; 最初の値だけが採用される
  • 33. gethash 関数の戻り値● 2 値の多値関数 ● 第一値: Key に対応する Value, Key が存在しない場合は nil ● 第二値: Key が存在した場合は t, 存在しない場合は nil(defvar dict (make­hash­table :test #equal))(setf (gethash "foo" dict) nil) ● 上のとき、 (gethash "foo" dict) → nil foo というキーが登録されていないから nil なのか? foo というキーに対応している値が nil なのか? –(defun has­key (dict key)  (multiple­value­bind (value has­key) (gethash key dict)    (declare (ignore value))    has­key)) ● 多値関数の戻り値を受け取っても使わない場合は「変数使ってない が大丈夫か?」とコンパイラが五月蝿いので declare を使って 「大丈夫だ。問題ない。」と伝えておくと吉。
  • 34. if フォーム● 分岐の基本なんだがイマイチな部分もある(if ( 条件テスト式 ) (then 節 ) (else 節 )) ● then 節と else 節はブロックじゃない。 1 個の式しか書けない ! – しょうがないので progn とか let とか使ってブロック化する。 ● Python で言う所の elif が無い! – ネストして頑張る。 – cond マクロを使う。 – case マクロを使う。 ● Python と違って if は " 文 " じゃない! – 値を返せるよ!戻り値は then 節とか else 節の式の評価値になる
  • 35. let とローカル変数● 正確には「レキシカル変数」と言う ● 呼称はまあいい。要するに let の範囲内のローカル変数だCL­USER> (let ((x 10) (y 20))           (print x)           (print y)           (let ((x "Hello"))             (print x)             (print y))) ● 実行結果は予想のとおり。 ● let と関数定義を組み合わせたりと色々な技がある ● let 以外にもレキシカル変数を取り扱うフォームは色々ある
  • 36. lambda 式● 関数の実体・無名関数 ● 関数を使い捨てしたいときとかに良く使う ● let と組み合わせてクロージャとか作ったりもする(setf (symbol­function foo) #(lambda (x) (+ x 1))) ||(defun foo(x) (+ x 1))foovaluefunc (function (lambda (x) (+ x 1)))prop Common Lisp のシンボルは値用と関数用の 2 つのスロットを持っ ているのだ!こういうタイプの Lisp を Lisp-2 という。ちなみに Scheme は値と関数のスロットは分かれていない Lisp-1 。 Python も Lisp-1 風。
  • 37. 文字列いってみよう!
  • 38. 文字列って実用では重要でも何故か多くの Lisp 本とかでの解説は少なめ
  • 39. Lisp の文字列● 実体は文字型の 1 次元配列 ● Common Lisp は文字型が存在する。文字型はエンコードとは独立 するように設計されている。が、まぁモゴモゴ… – C の char とは違う。 Java の char に似た立ち位置と思えばいい。 – 文字をリテラルとして書く場合は #A とか #Newline とか書く。 – Python には文字型は無い。 1 文字の文字列として扱うか、エンコードされた整数コード として扱っている ● リテラルで書く場合は "Hello World" のようにダブルクォート で囲んで書く。 Hello のようにシングルクオートはダメ ● Common Lisp の文字列は mutable に振る舞う。取扱い注意。
  • 40. 文字列が mutable なので● こんなことができちゃいます(defvar s1 "Hello World")(defvar s2 s1)(eq s1 s2)(setf (char s 5) #­)s1s2(eq s1 s2) ● REPL 環境で実験してみて! ● シンボル s1 と s2 は「同一の」文字列を参照している ● setf はやっぱりポインタ操作でしょ? ● メモリ上の文字列を直接書換えてるイメージなわけですよ。
  • 41. 文字列の操作● 文字の配列なので配列の操作関数が使える ● さらに配列はシーケンスの一種なのでシーケンス操作関数が使える ● なので、文字列特有の解説が少ないのかも ● 文字列用に特別に準備された関数もいくつかあるぱいそんs = "Hello World"s[3]s[3:5]s = s.strip()s = s.lower()りすぷ(defvar s "Hello World")(char s 3)(subseq s 3 5)(setf s (string­trim (#Space #Tab #Newline) s))(setf s (string­downcase s))
  • 42. split を作る(defmacro while (test­exp &body body)  `(do () ((not ,test­exp)) ,@body))(defun string­split (target­str &key (separators (#Space #Tab)))  (let ((result nil)        (startpos 0)        (curpos 0)        (endpos (length target­str)))    (while (< curpos endpos)      (if (member (char target­str curpos) separators)          (progn            (setf result (cons (subseq target­str startpos curpos) result))            (setf startpos (+ curpos 1))))      (setf curpos (+ curpos 1)))    (setf result (cons (subseq target­str startpos endpos) result))    (nreverse result))) loop マクロ使って格好良くもできるけど分かり易さ重視のベタ実装。使い方 にしても、ちょっと不格好過ぎ。コンパイル結果は以外といい。CL­USER> (string­split "Hello World foo bar  波浪ワールド ")("Hello" "World" "foo" "bar" " 波浪ワールド ")
  • 43. 関数のキーワード引数● Python と同じようにキーワード引数が使えるよ ● 関数の引数部分に &key というマークに続けて書く ● デフォルト値を与える&与えないの選択可ぱいそんdef foo(x, y, z=100): return x + y + zfoo(10, 20)foo(10, 20, z=50)りすぷ(defun foo (x y &key (z 100))    (+ x y z))(foo 10 20)(foo 10 20 :z 50)
  • 44. cons 関数 リストは cons セルで構成されている。 (X Y) (X . Y) セルの左側を car セルの右側を cdr と呼ぶ。長大な薀蓄を聞きたいのでなけれ ば、なんで left, right とかじゃないのかと X Y X Y いう質問はしないこと。(cons 1 (a b)) cons 関数は新しい cons セルを用意して、第一引 数を car 部に第二引数を cdr 部にセットする。 これを繰り返せば、どんな複雑なリストの構造も作 成できる。 1 まぁ実際は cons だけでは面倒くさいので色々と便 利関数は用意されている リストのあれこれ tips については後日っす。 a b
  • 45. cons をもう少しL1 L2 X Y 1 2 (cons l1 (cons l2 nil)) → ((x y) (1 2)) X Y 1 2
  • 46. setf をリストに使うときの注意● リストに対して setf してみると…CL­USER> (defvar lst (a b c))CL­USER> (setf (cdr lst) (1 2))CL­USER> lst ● 上の結果はどうなるか?lst A B C 1 2
  • 47. setf をリストに使う時の注意 ● 新しいセル作らずに無理やりポインタ書換え! CL­USER> (defvar lst (a b c)) CL­USER> (setf (cdr lst) (1 2)) CL­USER> lstlst (cdr lst) の位置 を強制的に書換えた! A B C これらのオブジェクトはもう参照 できない!いずれ GC の餌食にな る 1 2
  • 48. 次はファイルの読み込み
  • 49. 結構簡単よ!ぱいそんwith open("/tmp/foobar.txt", "r") as input_file: for line in input_file: print lineりすぷ(with­open­file (input­file "/tmp/foobar.txt" :direction :input) (loop for line = (read­line input­file) while line do (princ (format nil "~a~%" line)) )) ● loop マクロは相変わらず魔法だけど何となく分かるでしょ? ● format 関数は超便利。後日まとめて機能を紹介するですよ
  • 50. コマンドライン引数の取得● *posix­argv* というグローバル変数に入ってる(defun main()  (dolist (arg *posix­argv*)    (princ (format nil "~a~%" arg))))(eval­when (:compile­toplevel :load­toplevel :execute)  (main)  (quit))$ sbcl ­­script argtest.lisp foo bar baz ● dolist フォームの使い方。上の例で十分に分かるかと。
  • 51. 課題演習「単語数え」プログラム もう作れるよね?

×