ここからダウンロードできます。
動作環境: CPython (>=3.3)、PyPy (>=3.2.1)
> purple # もしくは python purple もしくは pypy purple purple> (def add100 (x) ...... (+ x 100)) <function fun at 0x1018c4c20> purple> (add100 11) 111 purple>
> cat test.purple (def add100 (x) (+ x 100)) (print 11) > purple test.purple 111 >
> purple -c test.purple > test.puc
> purple -e test.puc
;整数 3 => 3 (type 3) => <class 'int'> ;type、isinstanceなどのPythonの組み込み関数のほとんどはそのまま使えます。 ;文字列 "string" => "string" (type "string") => <'str'> ;シンボル 'symbol ;=> symbol ;キーワード :keyword ;=> :keyword ;真偽値 True ;=> True False ;=> False ;無 None ;=> None ;四則演算 (+ 1 2) ;=> 3 (* 2 5) ;=> 10 (+ "abc" "def") ;=> "abcdef" ;注意: ;+や-などの演算子は関数ではありません。 ;add,sub,mulなどといった、 ;各演算子に対応した関数が別にあります。 ;これらの関数はPythonのOperatorモジュールの関数です。 ;Purpleでは組み込み関数として使えるようになっています。
;グローバル環境でシンボルfooに"foo"を束縛 (val foo "foo") ;引数arg1を表示する関数funを定義 (def fun (arg1) (print arg1)) ;関数funを適用 (fun foo) ;-> "foo" ;=> None ;ローカル環境(関数定義内)で束縛 foo ;=>"foo" (def fun2 (arg1) (val foo arg1) (print foo)) (fun2 "bar") ;-> "bar" ;=> None foo ;=> "foo" ;ローカル環境で束縛(let) (let ((foo "a") (bar "b")) (print foo)) ;-> "a" ;=> None foo ;=> "foo" ;無名関数 ((fn (x) (+ x 100)) 11) ;=> 111 (|+ _ 100| 11) ;=> 111 (|+ _1 _2 100| 1 10) ;=> 111
;可変長引数 ;"& "の後ろが可変長引数 (def func1 (first & rest) rest) (func1 1 2 3) ;=> (2 3) ;可変キーワード引数 ;"&& "の後ろが可変のキーワード引数 (def func2 (&& kwargs) kwargs) (func2 :arg1 1 :arg2 2) ;=> {:arg1 1 :arg2 2} ;可変キーワード引数はテーブル(不変な辞書) として取得されます。 ;引数のキーワード指定 (def func3 (x y) y) (func3 1 :y 2) ;=> 2 (def func4 (x y) x) (func4 :x 1 :y 2) ;=> 1 ;分配束縛 (val (one two) '(1 2)) one ;=> 1 two ;=> 2 (val (one & two-three) '(1 2 3)) two-three ;=> (2 3) (val ((one two) three) '((1 2) 3)) one ;=> 1 three ;=> 3 ;letでの束縛も同様に分配束縛可能です。
;タプル '(1 x 3) ;=> (1 x 3) (val x 2) (tuple* 1 x 3) ; ←のショートカットは [1 x 3] ;=> (1 2 3) [1 x 3] ;=> (1 2 3) (+ '(1) '(2 3)) ;=> (1 2 3) (car '(1 2 3)) ;firstも同じ ;=> 1 (cdr '(1 2 3)) ;restも同じ ;=> (2 3) (get '(1 2 3) 0) ; getは実行効率のため関数ではありません。対応する関数はgetitemです。 ;=> 1 (cons 1 '(2 3)) ;=> (1 2 3) ;Lispスタイルのリンクリスト (cell* 1 2 3) ;=> L(1 2 3) (cons 1 '(2 3)) ;=> L(1 2 3) (cdr (cell* 1 2 3)) ;=> L(2 3) ;文字列 "string" (+ "abc" "def" "ghi") ;=> "abcdefghi" (get "ghi" 0) ;=> "g" (get "ghi" (slice 0 2)) ;=> "gh" ;テーブル(不変な辞書) (val t {:first-name "太郎" :last-name "山田"}) (:first-name t) ;=> "太郎" (t "first-name") ;=> "太郎" (val t2 {:middle-name "P."}) (:middle-name t2) ;=> "P." (val t3 (+ t t2)) ; -も使えます。 ;=> {:first-name "太郎" :middle-name "P." :last-name "山田"} ;レコード(名前付きタプル) (record Pos (x y)) (val p (Pos :x 1 :y 2)) ; (val p (Pos 1 2)) でも同じ。 p.x ;=> 1 (getattr p "x") ;=> 1 (get p 0) ;=> 1 (isinstance p Pos) ;=> True (isinstance p tuple) ;=> True (isinstance p str) ;=> False ;レコード - 継承 (record Pos3D Pos (z)) (val p3d (Pos3D :x 1 :y 2 :z 3)) (isinstance p3d Pos) ;=> True (isinstance p3d Pos3D) ;=> True ;レコード - メソッド(メソッドも継承されます) (record Pos2 (x y) (def print (self) (print (str self.x) + ", " + (str self.y)))) (val p2 (Pos2 1 2)) (p2.print) ;-> 1, 2 ;data式で関係のあるレコードを共通の親レコードでまとめて定義できます。 (data Pos (Pos2D x y) (Pos3D x y z)) ;上記は下記と同じです。 (record Pos ()) (record Pos2D Pos (x y)) (record Pos3D Pos (x y z)) (data Tree (Node left right) (Leaf value)) ;上記は下記と同じです。 (record Tree ()) (record Node (left right)) (record Leaf (value)) ;遅延シーケンス ;イテレータを遅延シーケンスに変換できます。 ;遅延シーケンスはイテレータと同様に必要になったときに値を生成しますが、 ;一度生成した値をメモリ上に保持するので、 ;通常のシーケンス(リスト)と同じように使用することができます。 (val ls (lazyseq (range 10000000000000000000))) (get ls 10) ;=> 10 (ls 12) ;lazyseqではこの形式でも値を参照できます。 ;=> 12 (get ls 0 10) ; スライスを取得する対応する関数はgetsliceです。 ;=> (0 1 2 3 4 5 6 7 8 9) (defseq ls2 (range 100000000000000000)) ;=> (val ls2 (lazyseq (range 100000000000000000)))と同じ意味です。
;Flask(http://flask.pocoo.org/)の使用例です。 (from-import flask Flask) ; python の from flask import Flask と同じです。 ; (import flask) (val Flask flask.Flask) も同じです。 (val app (Flask "demo")) ; Python 3.4.0 では、Python のバグでエラーになります。 @(app.route "/") (def hello () "Hello World!") (app.run) ;ブラウザでhttp://127.0.0.1:5000/を開くと ;Hello World!と表示されます。
;ifはArc()のifと同じです。 (if a b c d e) ;上のif文ではaが真ならb、そうではなくcが真ならd、aもcも真でないならeを評価した値になります。 ;(if a b)のように引数が偶数個の形式も書けます。 ;doは一つ以上の式を順次評価します。do構文全体の値は、最後の式を評価した値です。 ;doはifと組み合わせて使うことを想定しています。 (if True (do (print "True") (+ 1 2)) (print "False")) ;-> True ;=> 3 ;forはpythonのfor文と同様です。 ;(forはeachでもOKです。) ;Pythonのfor文と同様に名前空間を新たに作りません。 ;for構文全体の評価結果はNoneです。 (for item (range 3) (print item)) ;-> 0 ;-> 1 ;-> 2 ;=> None ;forの変数部分のみ再代入を許しています。 ;下記のような再代入はコンパイル時エラーとなります。 (val value 100) (for value (range 3) (print value)) ;=> valueの重複定義エラー (for value2 (range 3) (print value2)) (val value2 100) ;=> vlaue2の重複定義エラー (for value3 (range 3) (val value3 100)) ;=> value3の重複定義エラー ;Arcのaccumマクロが使えます。 ;http://arclanguage.github.io/ref/list.html#accum (accum accfn (each x '(1 2 3) (accfn (* x 10)))) ;=> (10 20 30) ;whileは一般的なwhileと同様です。 ;while構文全体の評価値はNoneです。 (while True (print "繰り返し"))
;末尾再帰でスタックを消費しないようにするデコレータ @recur (def facti (n a) (if (= n 0) a (facti (- n 1) (* n a)))) (def fact (n) (facti n 1)) (fact 10000) ;=> 28242294079...(長いので省略) ;引数リストの左側からカリー化するデコレータ ;(右側からバージョンのrcurriedもあります) @curried (def add_ (x y) (+ x y)) (val add100 (add_ 100)) (add100 11) ;=> 111 ;partial関数を明示的に呼ばなくても自動的に部分適用するデコレータ @auto-partial (def add-n (x n) (+ x n)) (val add-100 (add-n 100)) (add-100 11) ;=> 111 ;LRUキャッシュでメモ化するデコレータ(Pythonのfunctoolsモジュールにあるデコレータ) @(lru_cache) (def prin (n) (print n)) (prin 100) ;-> 100 ;=> None (prin 100) ;引数100に対応する結果の値がキャッシュされているため、関数本体が実行されることなくキャッシュされた値(None)が返ります。 ;=> None
(map |* _ 10| '(1 2 3)) ;=> <map object at 0x102306e90> (tuple (map |* _ 10| '(1 2 3))) ;=> (10 20 30) (tuple (mapchain |tuple* _ (* _ 10)| '(1 2 3)))) ;=> (1 10 2 20 3 30) ;readlinesはファイルの先頭から行を順番に返すイテレータを返す。 ;返ってきたイテレータがイテレートし終わると、自動的にファイルが閉じられます。 (let ((lines (readlines "test.txt"))) (for line lines (print line :end ""))) ;-> (text.txtの各行が表示される) ;=> None
(reduce |* _1 _2| '(1 2 3 4 5)) ;=> 120 ; 下記のスレッディングマクロはClojureと同様です(たぶん)。 (-> "1" (+ "23") (+ "4")) ;=> "1234" (->> "1" (+ "23") (+ "4")) ;=> "4231"
; タプル内包表記 (tuple-of (* x x) (x (range 10))) ;=> (0 1 4 9 16 25 36 49 64 81) (tuple-of (* x x) (x (range 10) :when (even x))) ;=> (0 4 16 36 64) ; リスト内包表記 (list-of (* x x) (x (range 10) :when (even x))) ;=> (0 4 16 36 64) ; yieldはPythonと同じです。 (def 10* () (while True (yield 10))) (val g (10*)) (next g) ;=> 10 (next g) ;=> 10 (def test () (yield "test")) (val t (test)) (next t) ;=> "test" (next t) ;=> エラー発生 (silent |next t|) ;=> None (ignore |next t| 100) ;=> 100 (on-err |print (type _)| |next t|) ;-> <class 'StopIteration'> ;=> None
(val v (ref 10)) ;=> refオブジェクト (get! v) ;=> 10 ;get!の省略形 #!v ;=> 10 ;set!現在値を引数として第2引数の関数を実行し、その値を第1引数のrefオブジェクトに設定します。 (set! v |+ _ 1|) ;=> 11 (get! v) ;=> 11
(module my-module (name print-name) (val name "my-module") (val name2 "my-module") (def print-name () (print name))) (my-module.name) ;=> "my-module" (my-module.name2) ;=> エラー (my-module.print-name) ;-> my-module
; ファイルのロード(コンパイル時) ; (load "filepath") (load "test.purple") ; ファイルのロード(ただし、同一ファイルのロードは一度だけ) ; (require "filepath") (require "test.purple") ; => test.purpleファイルのロード(コンパイル時) (require "test.purple") ; => 何もおこらない。
; (match <マッチ対象> ; <パターン> <パターンマッチしたときに実行される式> ; ...) ; match式は上から順番にパターンにマッチ対象がマッチするかを判別し、 ; マッチしていらたら対応する式を実行し、その値を返します。 ; いずれのパターンにもマッチしない場合は、Falseを返します。 ; 以下に各パターンの例を適当に記載します。 ; 各パターンは組み合わせることが可能です。 ; たとえば、シーケンスパターンの中にマップパターンなど。 ; なんでもマッチするパターン ; _ は任意の値にマッチします (match '(1 2 3) _ "true") ;=> "true" ; シーケンスパターン ; タプル、リスト、文字列、レコードなどにマッチします。 (match '(1 2 3) (1 2 3) 100) ;=> 100 (match '(1 2 3) (2 3 4) 200 (1 2 3) 100) ;=> 100 (match '(1 2 3) (1 & x) x) ;=> (2 3) (match '(1 2 3) (1 x 3) x) ;=> 2 (record Pos (x y)) (val p (Pos 1 2)) (match p (1 v) v) ;=> 2 ; レコードパターン (match '(1 2) (Pos 1 2) True) ;=> False (match (Pos 1 2) (Pos 1 y) y) ;=> 2 (match (Pos3D 1 2 3) (Pos 1 2 3) True) ;=> True ; レコードの継承関係と構造がマッチするためTrue ; マップ(テーブル)パターン (match {:x 3 :y 4} {:x 3 :y y} y) ;=> 4 ; 関数パターン (match '(1 2 3) |= _ '(1 2 3)| 100) ;=> 100 (match '(1 2 3) |= _ '(1 2 3)| m1) ;=> (1 2 3) (match '(1 2 3) (|= _ 1| |= _ 2| 3) m2) ;=> 2 ; def/match (def/match test (x y z) z (x) x) ; 上記は下記と同一 (def test (args) (match args (x y z) z (x) x)) (test 1) ;=> 1 (test 1 2 3) ;=> 3 ;defmはdef/matchと同じです。 ;以下、defmを使います。 ; TODO 型パターンやandパターン、orパターン、fnパターンなどの説明を書く。 (defm plus ((int a) (str b)) (+ (str a) b) ((str a) (int b)) (+ a (str b)) ((int a) (int b)) (+ a b) _ "error!") ;=>(plus 1 2) ;=> 3 (plus 1 "2") ;=> "12" (plus "1" "2") ;=> "error!" (plus "1" 2) ;=> "12"
(mac defseq (name iterable) `(val ,name (lazyseq ,iterable))) ; 参考)https://www.hackerschool.com/blog/13-list-comprehensions-in-eight-lines-of-clojure (mac tuple-of (& form) (val (bodyexpr bindingform) form) (if (= (len bindingform) 0) `(tuple* ,bodyexpr) (do (val (binding seqexpr & bindings) bindingform) (if (= binding ':when) `(if ,seqexpr (tuple-of ,bodyexpr ,bindings)) `(mapcat (fn (,binding) (tuple-of ,bodyexpr ,bindings)) ,seqexpr)))))