ここからダウンロードできます。
動作環境: 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)))))