Arcチュートリアル

Lispサイコーとかの言動がステキなポール・グラハム御大のhttp://arclanguage.org/:Arcが出たらしいのでチュートリアルを読んでみる。

英語がろくに読めないけど。
Lispは、schemeにちょっと手を出して挫折した経験しかない。

チュートリアルをざっと読んだ限りArcの特徴として

  • defで関数定義?
  • = で変数バインド
  • fnがlambda
  • letが、単一変数専用。複数はwith
  • 関数と変数が同じ名前空間?にある?
  • 文字列変数自体が関数になるような、Cの文字配列みたいな文字列アクセス
  • 割と破壊的な関数がデフォルト? pop push ++ hashtableとか
  • uniqオペレータを使って、他と絶対に被らない変数が作り放題。
  • マクロ・マクロ・マクロ
  • デフォルトでウェブアプリ作成可能。HTTPサーバになる!

触ってみて、

  • 重い。
  • CPU100%食うのを誰かNativeのArc書いて解決して。

以下、途中で投げ出した上に基礎的なことが分かってないひどいメモ
後で読んで書き直そう




http://ycombinator.com/arc/tut.txt

これは、Arcについての手短なチュートリアルです。プログアムの経験がほとんど
無く、Lispについての経験がまったく無い人を想定してかかれてます。

# うそこけー

それゆえこれは、Lispへの導入でもあります。


Arcプログラムは[expressions]からなります。もっとも単純な[expressions]は、
数値や文字列からなる--そ
れ自体を評価する--ものです。

arc> 25
25
arc> "foo"
"foo"

いくつかの括弧(parentheses )
に包み込まれる[expressions]もまた、[expressions]
です。これらは、
[lists]と呼ばれます。[lists]が評価されるとき、[elements]は、左から右へ評
価され、最初の値は(たぶん
関数です)残りの値passedされます。 [lists?
expression?]が返すものは何でも[expressions]
の値として返
されます。

arc> (+ 1 2)
3

ここで何が起こったか見てみましょう。最初に +、それから、1、2が評価され、
足し算関数(+)、1、2がそれぞれに返されます。1,2は足し算関数を通過し、3
になり、全体の
[expressions]の値として返されます。

(マクロはねじれを生じさせます。なぜなら、マクロは[lists]が評価される前に[lists]
を変形させるからです。それらのことについては後で触れることにします。)

[expressions]と[evaluation](評価)が再帰的に定義されることにより、プログラ
ムは、あなたの望むがままに複雑にすることが出来ます。

arc> (+ (+ 1 2) (+ 3 (+ 4 5)))
15

数字の前に + を置くことは、普通 "1 + 2"とか書くので奇妙に見えますが、+が
二つだけでなく、いくらでも引数として取れるという利点があります。

arc> (+)
0
arc> (+ 1)
1
arc> (+ 1 2 3 4 5 6 7 8 9 10)
55

このひっくり返しは、特にコードを生成するときに都合がいいものです。Lisp
共通することなんですが。

Arcに似ているLispの文法では、あるデータ型(たいていの言語でそうなってない
んですが)Symbols があります。
既に見てきたように + は、Symbolです。Symbolsは、数字や文字列がそうである
ようには、それ自身を評価しません。SymbolsはSymbolsが割り当てられた何らかの値を返します。

Symbol foo に13という値を与えて見ましょう。そうすれば、それが評価された
ときに13という値が帰ってくるでしょう。

arc> (= foo 13) # scheme だと、(define foo 13) とかになるのかね。
13
arc> foo
13

シングルクウォート ' をexpressionに配置することによって、評価させないこと
も出来ます。 そいで、'foo は Symbol fooを返します。

arc> 'foo
foo

実際問題、注意深い読者は fooの前に出てきた要素である = は、なんじゃらほい
と思うかもしれません。 要素が左から右へ評価されるんなら、なんで、これはfooが評価されたときにエラーを出さないのか?と。
普通の評価ルールに背くoperatorがいくつかあり、= もその一つです。
最初の引数は評価されません。

もし、listを引用すれば、('をつければ)listそのものが帰ってきます。

arc> (+ 1 2)
3

arc> '(+ 1 2)
(+ 1 2)


最初のexpressionでは、3という数字が返ってきてます。二つ目のは、'がついて
るので、symbol + と 数字
1,2からなるlistが返ってきます。

consを使って、新しい要素が前にくっついたlistを作り出すことが出来ます。

arc> (cons 'f '(a b))
(f a b)

元のlistは変わりません。

arc> (= x '(a b))
(a b)

arc> (cons 'f' x)
(f a b)

arc> x
(a b)

空のリストは nil というSymbolで表現されます。 nilはそれ自身評価されるよう
に定義されてます。(数字
とか文字列みたいに)つーわけで、一つの要素からなるlistを作るためにこう書
きます。

arc> (cons 'a nil)
(a)

car と cdrを使って listを分けることが出来ます。そいつらを使うと最初の要素
と、最初の要素を除いた全部がそれぞれ返ってきます。

arc> (car '(a b c))
a
arc> (cdr '(a b c))
(b c)

たくさんの要素を持ったlistを作るために consの連なりが作ったlistを使います

arc> (list 'a 1 "foo" '(b))
(a 1 "foo" (b))
arc> (cons 'a (cons 1 (cons foo" (cons '(b) nil))))
(a 1 "foo" (b))

注意。 listはどんな型の要素でも持つことが出来ます。

4つの括弧の組が最後のconsが呼ばれるまでに使われてます。 Lispプログラマー
は、これらにどうやって気
をつけてんでしょうか?
結論から言えばやってません。右括弧をexpressionsから、つけたりひいたりして
ますが、気にしたりしてま
せん。彼らは、インデントによってコードを読んでます。
括弧じゃないです。そしてコードを書くときエディタに任せてます。
(vi だったら、:set sm Emacsだったら M-x lisp-modeてな感じです。)

CommonLispの代入演算子に似てますけど、Arcの = は、変数だけに限るものでは
ありません。構造体の中身にも使えます。次のように使ってlistを変更することが出来ます。

arc> x
(a b)
arc> (= (car x) 'z)
(z b)
# えー
# guile clisp 不可
# goache OK だった

listはexploratoryなプログラミングで有用です。なぜなら、めっさ柔軟だからで
す。すぐにlistが表現できることについて厳密に賛成してもらう必要はありません。たとえば、平面上の点を表現する点を表す二つの数値からなるlistを使うことが出来ます。何人かの人は、xとyの二つからなるもっとマシな点の定義を考えるかもしれません。しかし、とりあえず、点を表すlistを使うことにしましょう。そうすっと、あなたのプログラムをn次元に拡張するとき、新しいコードを作るときにすべきこととして、無い座標を付け加えることはまったく必要ありません。のこりのplanar?コードはそのまま動くでしょう。

あるいは、もし別の方向に拡張し、部分的に評価される点を許すならば、点の組
として扱うことにした変数を表現するsymbolを使うことによって今あるコードはそのまま動くだろう。
#実例がないからよーわからん。自分なら動かないように実装してしまいそうだけど。

exploratoryなプログラミングでは、早すぎる局所化を避けることは、早すぎる最適化を避けることと同じくらい重要です。

listで表現できるもっともエキサイティングなものは、コードです。consで構成したlistは、プログラムが生成したlistと同じです。このことが意味するところは、プログラムを生成するプ
ログラムをあなたが書けるということです。これを行うフツーの方法は、マクロと呼ばれるものを呼ぶことです。このことは後で触れます。まずは、関数です。

いくつかの関数を既に見てきました。たとえば、+ cons car cdrとかです。defをつかって新しい関数を定義でき、定義した名前と、パラメータを表すリスト(もりかしたらゼロ個か、本体から呼ばれるもっとたくさんのexpressionsかもしれません)を使うことで新しいsymbolを得ることが出来ます。
関数が呼ばれるとき、それらのexpressionsは、順番で、本体で一時的に関数の引
数としてセットされた(bound 束縛された)Symbolを用いて評価されるでしょう。最後のexpressionsが返すものは何でも、関数コールの結果、「値として」返されます。

ここで、二つの数字をとり、それらの平均を返す関数を見てみましょう。

arc> (def average (x y)
(/ (+ x y) 2))
#
arc> (average 2 4)
3

関数の本体は一つのexpressionから成り立ってます。(/ (+ x y) 2)一つのexpressionからなることは全ての関数について共通しています。(純粋な関数コード(副作用の無いコード)において、常にそうです。

defや、=のような関数に関して注意すべきことは、全ての引数を評価しないということです。それは、独特のルールを持つもう一つの演算子群です。

defからなるexpressionが値として返している奇妙なobjectはなんでしょうか?
# のそれは関数がそう見えるものです。ArcやたいていのLisp方言において、においては、関数はデータ型の一つです。数字や文字列と同じように。

文字列の表記上の表現において、ダブルクオートに囲まれているように、関数の表現は、fn symbolを含むリストとして表現されます。fnの後にはそいつの引数とさらに本体が続きます。そ
いで、二つの引数の平均を返す関数が表現されるわけです。

arc> (fn (x y) (/ (+ x y) 2))
#

ここでは、他のとっから呼べる意味的な特定の名前のついた関数がありません。全ての def は基本的に次の動きと同様です。

arc> (= average (fn (x y) (/ (+ x y) 2)))
#

そしてもちろん、どこでもひとつのシンボルを使いたいときは使えます。次みたいに。
#うまく訳せないけど、名前がついてなくても、定義したらそのまま呼べるつーことだろう。
arc> ((fn (x y) (/ (+ x y))) 2 4)
3

このexpressionは、三つの要素を持っています。平均を返す関数を作り出す (fn (x y) (/ (+ x y) 2))と数字の2と4です。そいで、三つのexpressions全てを評価し、2と4を平均を返す関数
に渡せば、3という結果を得ることが出来ます。

関数は表示するのが面倒くさい。今書いたものがそのまま関数だからだ。(超訳

# その理由は、関数がクローじゃでしかありえないから。クロージャを表示するのはトリッキーな問題だ。

Arcでは、データ構造は関数があるところならどこでも使われ、そこでストアされているものならなんにでもに対するインデクスから関数として振舞う。
そういうわけで、最初の要素になってる文字列を使うとこうなる。

arc> ("foo" 0)
#\f

この戻り値は、実際、文字に見える何かである。

# Cの文字列配列みたいな感じで使えるつーことだろう。


関数的な位置におけるデータ構造を持つExpressionは最初の引数の=見たいに動く

arc> (= s "foo")
"foo"
arc> (= (s 0) #\m)
#\m
arc> s
"moo"

# Cの文字配列みたいなもんらしい。確かにschemeには無い。と思う・・・
# my注 Cにおけるcharのリテラルが 'a'であるように、Lisp系の言語では、#\a
と書くらしい。


一時変数を作るのに、よく使われる二つの演算子がある。let と with。

arc> (let x 1
(+ x (* x 2)))
3

schemeでは、(let ((x 1))
# (+ x (* x 2))) こうなるな。
# 複数個の自動変数をBindしたけりゃ、
# (let ((x 3) (y 4))
# (sqrt (+ (expt x 2) (expt y 2))))

複数の変数を束縛するためには withを使う

arc> (with (x 3 y 4)
(sqrt (+ expt x 2) (expt y 2))))


そいでここまでに、われわれは既に暗黙的に評価結果を表示する手段を得ている

評価の途中結果を表示する標準的な方法は、pr または prnを使用することだ。
それらの印字する関数は複数の引数をとり、引数を順番に表示する。prnは、次の
行に行く。
Javaの print と println の違いっぽい

ここで、averageでの使用例

arc> (def average (x y)
(prn "my arguments were: " (list x y))
(/ (+ x y) 2))

redefining average

#
arc> (average 100 200)
my arguments were: (100 200)
150



****if が変。というはなし****

(if a b c d e)が
(if a
b
(if c
d
e))
ってことになるらしい。
# 逆に分かりにくくね?

もし、他の言語でelseifとかつかったことがあるなら、このパターンが似たよー
なもんということでいい?
# えー

ifがとるそれぞれの引数は単一のexpressionです。そいで、もしテストの結果に
依存するような複数のモノが
書きたかったら、doを使って連結してね。
# えー。ってschemeもそうだったっけ。beginとか

arc> (if (odd 1)
(do (prn "hello")
(+ 2 3)))

しかし、whenつーのもあります。# 使い方わかんねー


and,or演算子もあります。

arc> (and nil
(pr "you'll never see this"))
nil

# 偽は nilっぽい(arc> (odd 2) → nil)ね。それ以外はなんでも真でいーのか

# いいようだ。
# arc> (if 'a
# "aaa"
# "bbb")
#"aaa"

negation 演算子 no もあります。
arc> (no nil)
t
arc> (no t)
nil


標準的な比較演算子は is です。
#原文の The standard comparison operator is is が笑える

arc> (is 'a 'a)
t

arc> (is "foo" "foo")
t

arc> (let x (list 'a)
(is x x))
t

arc> (is (list 'a) (list 'a))
nil
schemeでも注意があった。つーかmzschemeで実装されてるんだから当たり前。

セルの中身まで比較して一致してるなーというときは、iso (isomorphic)演算子
arc> (iso (list 'a) (list 'a))
t

inが使えます。

(let x 'a
(in x 'a 'b 'c))
t

case 演算子

使い方は以下

arc> (def translate (sym)
(case sym
apple 'mela
onion 'chpolla
'che?))
#
arc> (translate 'apple)
mela
arc> (translate 'syzygz)
che?

# 使われてる単語の意味が分かりません。

Arcには繰り返し演算子があります。数字の範囲に使える for

arc> (for i 1 10)
(pr i " "))
1 2 3 4 5 6 7 8 9 10 nil

listや文字列の要素には each

arc> (each x '(a b c d e)
(pr x " "))
a b c d e nil

# えー nilまで出るの?


whileもあります。

arc> (let x 10
(while (> x 5)
(= x (- x 1)
(pr x)))

98765nil

Cのforみたいなのは実際あんま使われないけど、もそっとシンプルな repeat演算
子もあるでよ
arc> (repate 5 (pr "la "))
la la la la la nil

#ここまで、必要かね?

map 演算子もあります。

arc> (map (fn (x) (+ x 10)) '(1 2 3))
(11 12 13)
lispschemeではおなじみだろうけど・・・・

引数のリストの数があってなくても大丈夫
arc> (map + '(1 2 3 4) '(100 200 300))
(101 202 303)

gaucheはいけた。guileは不可みたいだね。

rubyのblockみたいに [... _ ...]が使えるよ。
arc> (map [+ _ 10] '(1 2 3))
(11 12 13)

# でも引数は_ の一個だけしか使えないの?ほんま?

変数をあんま使わないことは、実際上プログラムを短くするには有効だ!
理屈の上では、不必要な変数があればそんだけプログラムのロード時間が長くな
るからね。


:を使って関数を合成できるぞ!
たとえば、(foo:bar x y)は
(foo (bar x y))とおんなじってコトさ!

合成関数は引数に使うと便利だぞ!

arc> (map odd:car '*1
(nil t nil t nil)

mapとよく似た関数で、keepってのを使うとなんだか楽だぞ!

arc> (keep odd '(1 2 3 4 5 6 7))
(1 3 5 7)

remはその逆だ!

arc> (rem odd '(1 2 3 4 5 6 7))
(2 4 6)

あと all some pos trues なんてのが標準で用意されてるぞ!
便利だから使え。

arc> (all odd '(1 3 5 7))
t
arc> (some even '(1 3 5 7))
nil
arc> (pos even '(1 2 3 4 5))
1
arc> (trues [if (odd _) (+ _ 10)]
'(1 2 3 4 5))
(11 13 15)

# truesがよー分からん。 原文は以下
# trues, which returns a list of all the non-nil return values:


remとかkeepとか比較演算子つかってよきに計らってくれます。

arc> (rem 'a '(a b a c u s))
(b c u s)
arc> (rem #\a "abacus")
"bcus"


リストはデータ構造のバラエティいっぱいな表現として使われます。
しかし、もし、key-pair(perlrubyのHash)を効果的に使いたければArcにはハ
ッシュテーブルがありま
す。

arc> (= airports (table))
#hash()
arc> (= airports "Boston") 'bos)
bos

もし、リストからハッシュテーブルを作りたければ listtabを使います。

arc> (let h (listtab '*2
2

キーから値を取り出すには

arc> (let h (obj x 1 y 2)
(h 'y))
2


引数をグループ化したり、Keyに'をつけなくていい短縮形
arc> (let h (obj x 1 y 2)
(h 'y))
2

# obj演算子について説明がほしい

listや文字列のように、ハッシュテーブルは関数があるところならどこでも使え
ます。

arc> (= codes (obj "Boston" 'bos "San Francisco" 'sfo "Paris" 'cdg))
#hash*3

arc> (map codes '("Paris" "Boston" "San Francisco"))
(cdg bos sfo)

keys関数は、ハッシュテーブルのキーを返します。
同様にvals関数は、値を返します。

arc> (keys codes)
("Boston" "Paris" "San Francisco")


maptableと呼ばれるハッシュテーブルのための関数があります。
それは、listに対するmapと似てます。
が、新しいテーブルの代わりに元のテーブルを返します。
# たぶん、破壊的操作をするよ。って意味だと。

(maptable (fn (k v) (prn v " " k))
codes)
sfo San Francisco
cdg Paris
bos Boston
#hash*4

[注意: 関数のように、ハッシュテーブルも出力されようがありません。が、直
してくれると嬉しいな。]

1960年のマッカーシーの論文以来のkey/valueのペアでリストを表現するという伝
統があります。

arc> (= codes '*5

こーゆーのは連想リスト、または、alist for short と呼ばれます。 私はかつて
alists
は、hackだと思っ
手いたことがありました。
# hack → hashの間違い?

しかし、連想リストをハッシュテーブルのように扱うにはあまりにも多くのこと
をしなくてはいけません。
ソートをじっそうしたり。なんやかや。
# よーわかりません。

alref 関数は、keyに対応するalistの中の最初の値を返します。
arc> (alref codes "Boston")
bos

文字列を構築するカップ演算子
#意味不明
があります。
最も一般的なのは、stringです。 複数の引数を取って文字列にします。

arc> (string 99 " bottles of " 'bee #\r)
"99 bottles of beer"

どの引数も pr で出力されるように見えます。ただし nilを除いて。

tostringって関数もあります。そいつは、
# 面倒くさいので以下を参照

arc> (tostring
(prn "domesday")
(prn "book"))
"domesday\nbook\n"

# to string ってわけですね。

typeを用いて、型を見ることも出来ます。
coerceをつかって新しい型に変換することも出来ます。

arc> (map type (list 'foo 23 23.5 '(a) nil car "foo" #\a))
(sym int num cons sym fn string char)

arc> (coerce #\A 'int)
65
arc> (coerce "foo" 'cons)
(#\f #\o #\o)
arc> (coerce "99" 'int)
99
arc> (coerce "99" 'int 16)
153

# coerceってのは rubyでも見たような。

push、pop演算子は、listをスタックのように扱います。
#そのまんまです。

arc> (= x '(c a b))
(c a b)
arc> (pop x)
c
arc> x
(a b)
arc> (push 'f x)
(f a b)
arc> x
(f a b)

# 破壊的なのねー。当たり前だけど。

= のよーなのは、データ構造の範囲内で動きます。ただの変数ではダメです。

arc> (push 'l (cdr x))
(l a b)
arc> x
(f l a b)

インクリメントやデクリメントには ++ -- を。
arc> (let x '(1 2 3)
(++ (car x))
x)
(2 2 3)

# (++ 1) はエラーになるぞ?? なんで??


There's also a more general operator called zap that changes something
to the result any function returns when applied to it. I.e. (++ x)
is equivalent to (zap [+ _ 1] x).

zapというもっと一般的なオペレータがあります。
そいつは、関数が適用された結果に対して何かを変えます。
# 超意味不明 英語分からん・・・
たとえば、 (++ x) は、(zap [+ _ 1] x)と等価です。



sort関数は、最初の引数として与えられた関数にしたがってソートされた並びの
コピーを返します。

arc> (= x '(9 8 7 2 5))
(9 8 7 2 5)

arc> (sort < x)
(2 5 7 8 9)

arc> x
(9 8 7 2 5)


arc> (zap [sort < _] x)
(2 5 7 8 9)
arc> x
(2 5 7 8 9)

# 破壊的だってことかしら。

正しい場所につっこみたかったら、insort
arc> (insort < 4 x)
(2 4 5 7 8 9)

実際、数字だけからなるlistを並び替えることなんてめったに無いので、次みた
いな形で使うと思うよ。
arc> (sort (fn (x y) (< (len x) (len y)))
'("orange" "pea" "apricot" "apple"))
("pea" "apple" "orange" "apricot")

Arcのソートは stable。それの意味するところは、元の並びを変えないってコト
さ。
arc> (sort (fn (x y) (< (len x) (len y)))
'("aa" "bb" "cc"))
("aa" "bb" "cc")

>や、<より、比較関数は必要になるので、Arcはcompre関数が実装してある。

arc> (sort (compare < len)
'("orange" "pea" "apricot" "apple"))
("pea" "apple" "orange" "apricot")

オプショナルな引数をとる関数をすでにいくつか見てきました。
オプショナルなパラメータをとる間巣を作るには、xの変わりに、(o x)と書けば
いいです。
オプションパラメータのデフォルトはnilです。

arc> (def greet (name (o punc))
(string "hello " name punc))
#
arc> (greet 'joe)
"hello joe"
arc> (greet 'joe #\!)
"hello joe!"


関数は好きなだけオプショナルなパラメータをとることが出来ますが、関数はパ
ラメータの終わりに到達する
必要があります。

もし、expressionをパラメータの名前の後におきたければ、デフォルトの値を生
み出すために必要なら、評価
されます。expressionは先行する値にアクセスできます。

arc> (def greet (name (o punc (case name who #\? #\!)))
(string "hello " name punc))

redefining greet

#
arc> (greet 'who)
"hello who?"

To make a function that takes any number of arguments, put a period
and a space before the last parameter, and it will get bound to a
list of the values of all the remaining arguments:

任意の数のパラメータを取る関数を作る場合、最後のパラメータの前にピリオド
とスペースをおくこともでき
ます。残りの全ての引数はリストととしてBindされます。

schemeにもある .記法とおなじ?

arc> (def foo (x y . z)
(list x y z))
#
arc> (foo (+ 1 2) (+ 3 4) (+ 5 6) (+ 7 8))
(3 7 (11 15))


This type of parameter is called a "rest parameter" because it gets
the rest of the arguments. If you want all the arguments to a
function to be collected in one parameter, just use it in place of
the whole parameter list.
このパラメータの型は、残りのパラメータ"rest parameter" #ってそのまんまか
いな、と呼ばれます。
関数に与えられたパラメータをまるごと扱いたければ、パラメータリストに換わ
りに使うといいかも。


(These conventions are not as random as they seem. The parameter
list mirrors the form of the arguments, and a list terminated by
something other than nil is represented as e.g. (a b . c).)
(これらの規約は見えるほどにでたらめではない。パラメータリストは引数をコ
ピーし、表現されてるnil
外の何かで終わる。例 (a b . c).)

リストの形で関数に適用する場合は、applyを使う。
arc> (apply + '(1 2 3))
6

applyを使って平均関数を書き直してみる。

arc> (def average args
(/ (apply + args) (len args)))
#
arc> (average 1 2 3)
2

つうわけで、マクロを使い始めるのに十分な知識を得た。# ほんま?
マクロは基本的にはコードを生成する関数だ。もちろんコードを生成することは
たやすい。 #ほんま?
たんに listをコールすればいい。
arc> (list '+ 1 2)
(+ 1 2)

マクロが示すものは、このように生み出されたコードをあなたのプログラムに挿
入する方法だ。ここに
(ちょっとしょうもないが)マクロの定義がある。

arc> (mac foo ()
(list '+ 1 2))

redefining foo

#3(tagged mac #)

ほとんど関数定義と女滋養に見えるかもしれないが、defのかわりにmacを用いて
くれ。

このマクロがいうことは、fooという表現があなたのコードに出てくるところはど
こでも、関数のようには評
価されない。そのかわり、マクロの本体部分の評価の結果(list '+ 1 2)に置き換
わる。これは、マクロ呼び
出しの展開と呼ばれる。

別の言葉でいれば、上記のようにfoo マクロを定義したら、fooを置いたどこでも
あなたのコードに (+ 1 2)
を置いたことと同じになる。

arc> (+ 10 (foo))
13

ここではむしろマクロなんか使わない方がいい。なぜなら、マクロが何の引数も
とってないから。
もうちょっと有用な例は以下。

arc> (mac when (test . body)
(list 'if test (cons 'do body)))

redefining when

#3(tagged mac #)

ここで初めから実装済みのwhen演算子を再定義してみた。
That would
ordinarily be an alarming idea, but fortunately the definition we
supplied is the same as the one it already had.

# よーわからん
# 元々在ったのと同じようにに使えるってことが言いたいのか。

arc> (when 1
(pr "hello ")
2)
hello 2

上記の定義が言うことは、最初の要素がwhenであるようなexpressionを評価しな
ければならないとき、以下の
適用の結果で置き換えるということだ。

(fn (test . body)
(list 'if test (cons 'do body)))

どうなるか見てみよう。

arc> (apply (fn (test . body)
(list 'if test (cons 'do body)))
'(1 (pr "hello ") 2))
(if 1 (do (pr "hello ") 2))

そういうわけでArcは、

(when 1
(pr "hello ")
2)

(if 1
(do (pr "hello ")
2))

に変形する。


***ここからバッククウォート ` を使うと簡単にかけるよ。という話****


Building up expressions using calls to list and cons can get unwieldy,
so most Lisp dialects have an abbreviation called backquote that
makes generating lists easier.

If you put a single open-quote character (`) before an expression,
it turns off evaluation just like the ordinary quote (') does,

arc> `(a b c)
(a b c)

`で引用されるリストのなかでカンマ , を使えば、評価される前に変数が展開さ
れるよ

arc> (let x 2
`(a ,x c))
(a 2 c)

@をつけるとlistが展開されるよ。# my注 最後まで展開されないぞ


arc> (let x '(1 2)
`(a ,x c))
(a 1 2 c)

backquateを使うとマクロ定義がすっきりするよ。

(mac when (test . body)
`(if ,test (do ,@body)))


実際、Arcのソースでもwhenは、そうやって定義してます。


One of the keys to understanding macros is to remember that macro
calls aren't function calls. Macro calls look like function calls.
Macro definitions even look a lot like function definitions. But
something fundamentally different is happening. You're transforming
code, not evaluating it. Macros live in the land of the names, not
the land of the things they refer to.

マクロを理解するコツの一つはマクロが関数コールとちがうということを覚えて
おくことです。マクロコール
は、関数コールに似ています。マクロ定義も多分に関数定義に似てます。しかし
、起こることは根本的に違い
ます。あなたはコードを変化させるのであって、評価(実行?)するのではありま
せん。マクロは名前の国に
住んでいますが、名前が参照するものの国には住んでません。

たとえば、この repeat の定義を見てください。

arc> (mac repeat (n . body)
`(for x 1 ,n ,@body))
#3(tagged mac #)

正しく動くように見えます?

arc> (repeat 3 (pr "blub "))
blub blub blub nil

しかし、これを在る文脈の中で使うと、変なことが起こります。

arc> (let x "blub "
(repeat 3 (pr x)))
123nil

マクロを展開して見ると一目瞭然です。次のコードと等価です。
(let x "blub "
(for x 1 3 (pr x)))

ここまでくるとBugは明らかです。マクロは変数xを繰り返しの間、カウンターと
して使います。そいで、xを
出力しようとします。


Some people worry unduly about this kind of bug. It caused the
Scheme committee to adopt a plan for "hygienic" macros that was
probably a mistake. It seems to me that the solution is not to
encourage the noob illusion that macro calls are function calls.
People writing macros need to remember that macros live in the land
of names. Naturally in the land of names you have to worry about
using the wrong names, just as in the land of values you have to
remember not to use the wrong values-- for example, not to use zero
as a divisor.
****マクロを追放したSchemeコミュニティのDIS?***

# 0で割り算する人がいねーんだから、ほっとけといいたいのか。

repeatのバグを直す方法はxの変わりにソースで出てこないSymbolを使うことです

Arcでは、 uniqという関数を呼ぶことで代わりと出来ます。
そrで、正しいrepeatの定義は(実際にArcでもそうやって定義してんですが)

(mac repeat (n . body)
`(for ,(uniq) 1 ,n ,@body))

If you need one or more uniqs for use in a macro, you can use w/uniq,
which takes either a variable or list of variables you want bound to
uniqs. Here's the definition of a variant of do called do1 that's
like do but returns the value of its first argument instead of the
last (useful if you want to print a message after something happens,
but return the something, not the message):
もし、マクロで、一つ、またはもっとたくさんのuniqが使いたい場合、w/uniqが
使えます。
w/uniqは、一つの変数、または、変数のリストをとり、uniqsに束縛します。
ここに、doのバリエーションの、最後の引数の評価を返却するdoと違い、最初の
引数の評価を返却するdo1の
定義を載せます。(何かが起こったあとにメッセージを表示させたいときに有用
ですが、メッセージではな
く、起こる何かを返します。)

# my注 >arc (do 1 2) → 2 / (do1 1 2) → 1

(mac do1 args
(w/uniq g
`(let ,g ,(car args)
,@(cdr args)
,g)))

# w/uniqの使い方がさっぱりわかんね

Sometimes you actually want to "capture" variables, as it's called,
in macro definitions. The following variant of if, which binds the
variable it to the value of the test, turns out to be very useful:

(mac aif (expr . body)
`(let it ,expr (if it ,@body)))

#なに言ってんだかさっぱり
#On Lispにもなんか書いてあった気がする。

In a sense, you now know all about macros-- in the same sense that,
if you know the axioms in Euclid, you know all the theorems. A lot
follows from these simple ideas, and it can take years to explore
the territory they define. At least, it took me years. But it's
a path worth following. Because macro calls can expand into further
macro calls, you can generate massively complex expressions with
them-- code you would have had to write by hand otherwise. And yet
programs built up out of layers of macros turn out to be very
manageable. I wouldn't be surprised if some parts of my code go
through 10 or 20 levels of macroexpansion before the compiler sees
them, but I don't know, because I've never had to look.

ある意味、マクロの全てをこれであなたは知ったことになります。
それはある意味ユークリッドの公理を知っていれば、全ての定理を導くことが出
来るとのおんなじ意味です。

# ちょっと待て。

これらの単純なアイデアのから膨大なものが導け、それらが定義する領域の探索
に何年も掛かるでしょう。
少なくとも私には。
しかし、行く価値のある道です。なぜならマクロは更なるマクロ呼び出しを展開
できるからです。
云々。


One of the things you'll discover as you learn more about macros
is how much day-to-day coding in other languages consists of manually
generating macroexpansions. Conversely, one of the most important
elements of learning to think like a Lisp programmer is to cultivate
a dissatisfaction with repetitive code. When there are patterns
in source code, the response should not be to enshrine them in a
list of "best practices," or to find an IDE that can generate them.
Patterns in your code mean you're doing something wrong. You should
write the macro that will generate them and call that instead.

マクロについてもっと亜棚が学ぶにつれて発見することの一つは、
他の言語での日々のコーディングにマクロ展開を生み出すことがどれくらいたく
さんあるか
ということでしょう。
云々。

# パターンを見つけ出してマクロに置き換えろって言ってるんすかね。



*******簡単にWebさーばになるよ!***********

Now that you've learned the basics of Arc programming, the best way
to learn more about the language is to try writing some programs
in it. Here's how to write the hello-world of web apps:

# まーとにかく書いて慣れろやっていってるんだと思われ。


If you now go to http://localhost:8080/hello your new web app will
be waiting for you.

See the sample application in blog.arc for ideas about how to make
web apps that do more.
****サンプルファイルのblog.arcには、もっとウェブアプリを作って実行す
るアイデアが載ってるよ**
**

***あらかじめ定義された手続きを載せとくね。***

(def cadr (xs)
(car (cdr xs)))

(def no (x)
(is x nil))

(def list args
args)

(def isa (x y)
(is (type x) y))

(def firstn (n xs)
(if (and (> n 0) xs)
(cons (car xs) (firstn (- n 1) (cdr xs)))
nil))

(def nthcdr (n xs)
(if (> n 0)
(nthcdr (- n 1) (cdr xs))
xs))

(def tuples (xs (o n 2))
(if (no xs)
nil
(cons (firstn n xs)
(tuples (nthcdr n xs) n))))

(def trues (f seq)
(rem nil (map f seq)))

(mac unless (test . body)
`(if (no ,test) (do ,@body)))

(mac awhen (expr . body)
`(let it ,expr (if it (do ,@body))))

(mac n-of (n expr)
(w/uniq ga
`(let ,ga nil
(repeat ,n (push ,expr ,ga))
(rev ,ga))))

These definitions are taken from arc.arc. As its name suggests,
reading that file is a good way to learn more about both Arc and
Arc programming techniques. Nothing in it is used before it's
defined; it is an exercise in building the part of the language
written in Arc up from the "axioms" defined in ac.scm. I hoped this
would yield a simple language. But since this is also the source
code of Arc, I've tried to balance simplicity with efficiency. The
definitions aren't mathematically minimal if that would be insanely
inefficient; I tried that once, and they were.

これらの定義は arc.arcにあります。
公理にあたるものは ac.scm

The definitions in arc.arc are also an experiment in another way.
They are the language spec. The spec for isa isn't prose, like
function specs in Common Lisp. This is the spec for isa:

(def isa (x y)
(is (type x) y))

It may sound rather dubious to say that the only spec for something
is its implementation. It sounds like the sort of thing one might
say about C++, or the Common Lisp loop macro. But that's also how
math works. If the implementation is sufficiently abstract, it
starts to be a good idea to make specification and implementation
identical.

I agree with Abelson and Sussman that programs should be written
primarily for people to read rather than machines to execute. The
Lisp defined as a model of computation in McCarthy's original paper
was. It seems worth trying to preserve this as you grow Lisp into
a language for everyday use.

Notes

[1] Note to Lisp hackers: If you're used to the conventional Lisp
cond operator, this if amounts to the same thing, but with fewer
parentheses. E.g.

(cond (a b)
(c d)
(t e))

becomes

(if a b
c d
e)

JMC's original cond didn't have implicit progn, so the parens around
each pair of clauses were unnecessary. They became necessary soon
after, however, when cond started to have implicit progn in the
first Lisp implementations. This probably prevented people from
realizing they hadn't originally been needed. But most conds in
the wild seem to occur in purely functional code, and thus pay the
cost in parens of implicit progn without actually needing it. My
experience so far suggests it's a net win to offer progn a la carte
instead of combining it with the default conditional operator.
Having to use explicit dos may even be an advantage, because it
calls attention to nonfunctional code.

[2] "Recursive Functions of Symbolic Expressions and Their Computation
by Machine, Part I," CACM, April 1960.

http://www-formal.stanford.edu/jmc/recursive/recursive.html

*1:1 2) (4 5) (7 9))) (t nil t) 否定演算子は ~ を名前の前に使うことでで代用できるぞ! arc> (map ~odd '(1 2 3 4 5

*2:x 1) (y 2))) (h 'y

*3:"Boston" . bos) ("Paris" . cdg) ("San Francisco" . sfo

*4:"Boston" . bos) ("Paris" . cdg) ("San Francisco" . sfo

*5:"Boston" bos) ("Paris" cdg) ("San Francisco" sfo))) (("Boston" bos) ("Paris" cdg) ("San Francisco" sfo