トップ «前の日記(2007-02-01) 最新 次の日記(2007-02-03)» 編集

じじぃの日記、ツッコミ可

Twitter: @jijixi_org
Xbox Live: jijixi

初心者が書いた OCaml 入門
Spotlight tips サイト内リンク集
1970|01|02|
2003|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|11|
2011|05|
2012|01|

2007-02-02 [長年日記]

% [Smalltalk] とりあえず Smalltalk で最初にびっくりすべきところ

% rlwrap gst
GNU Smalltalk ready

st> 2 * 4 + 1!
9
st> 1 + 4 * 2!
10

な、なんだってー!?

要は演算子だろうが何だろうが全部メッセージで、優先順位とかは特別扱いしないよ…ってことなんだろうけど、知らないとビビるよ、これ。 ある意味 LISP に近いかも。 優先順位関係無しに、先に計算されるべきところはカッコで囲まざるを得ないところが。 まあ、算数へのこだわりが無ければ、こっちの方があいまいさが無くて嬉しいという見方もできるけど。

……実はわし、結構こういう状況でかけ算のところをカッコで囲みたくなるタイプなのよね。 「えーと、まずかけ算が先で…」とか考えるのがめんどくさいっつーか(苦笑)。 さすがに恥ずかしさがあって、結局カッコは付けないんだけど。

ちなみに上記の例における "!" は Smalltalk の文法的には必要の無いもののはずで、GNU Smalltalk の対話環境で「ここまで入力したものを評価しろ」という合図でしかない。 OCaml で言うところの ";;" みたいなもんか。

% [Smalltalk][Scheme][Dylan] 所詮、関数型言語とかオブジェクト指向言語なんて何を基準に抽象化するか (しやすくチューニングされてるか) の違いしか無いのさ (極論)

やー、なんかメッセージ式がカッコの無い LISP に見えるというか何というか。

% cat add.scm
(define (f . args)
  (let-keywords* args ((x 0)
                       (y 0))
                 (+ x y)))

(print (f :x 1 :y 2))
% gosh add.scm
3

こんなのがあるとして、

% cat add.st
Object subclass: #F
   instanceVariableNames: ''
   classVariableNames: ''
   poolDictionaries: ''
   category: nil.

! F class methodsFor: 'adder' !
x: arg1 y: arg2
   ^(arg1 + arg2)
!!

(F x:1 y:2) printNl.
% gst add.st
3

同じじゃね? まあネタだけど。

それはそれとして、メソッド定義のときの "!" の使い方がイマイチしっくり来ない。 なんつーか、どこで必要でどこで必要無いのかがよくわからんというか。 とりあえず、適当に試してみた感じだと、F class に methodsFor を送ってる前後にはどうしても必要っぽい。 あとメソッド定義の後の二個も (これが特によくわからん)。 ともあれ、よくわからんので、そういうもんだと憶えてしまうしかないのかも。

ちなみにおまけで Dylan バージョン。

% cat add.dylan
module: add

define function f(#key x, y)
   x + y
end;

define function main(name, arguments)
   format-out("%d\n", f(x:1, y:2));
   exit-application(0);
end function main;

// Invoke our main() function.
main(application-name(), application-arguments());
% ./add
3

Dylan のキーワード引数の扱いはステキすぎると思う。 特にキーワードが :x じゃなく x: なところがポイント。 オプショナル引数だけじゃなく、必須引数にもキーワード付けられるし。 OCaml のラベル引数みたいに変なゴミ (~) 付けなくて良いし。

% [Smalltalk] 新しい言語を触るとき、ついつい調べてしまうこと

それは末尾再帰が最適化されるかどうか。(ちょっと病気です)

% cat tail_recursion.st
!Object methodsFor: 'tail recursion test'!
f: aNum
   (aNum <= 0)
      ifTrue:  [ ^ 'end.' printNl ].
   self f: (aNum - 1)
!!

|o|
o := Object new.
o f: 100000
% gst tail_recursion.st
"Global garbage collection... done, heap grown"
"Global garbage collection... done, heap grown"
"Global garbage collection... done, heap grown"
"Global garbage collection... done, heap grown"
"Global garbage collection... done, heap grown"
'end.'

うーん、なんじゃこりゃ。 メソッド呼び出しにスタック使ってないってこと? それとも単にスタックがデカイだけか? ちなみに再帰の回数を 1,000,000 にすると、

% gst tail_recursion.st
"Global garbage collection... done, heap grown"
"Global garbage collection... done, heap grown"
"Global garbage collection... done, heap grown"
"Global garbage collection... done, heap grown"
"Global garbage collection... done, heap grown"
"Global garbage collection... %

GC に失敗したらしくて、変なとこで止まった。 でも、そもそもあのコードでヒープフルってことは末尾再帰は最適化されてない気がするんだけど、スタックのサイズに上限が無いってことなのかね。

% [Smalltalk] コードブロック

要するに Ruby の Proc オブジェクトかな?

st> !Object methodsFor: 'test'!
Object
st> adder: x
st>   ^ [ :y | x + y ]
st> !!
st> Smalltalk at:#obj put:(Object new)!
Object new "<0x2036c78>"
st> Smalltalk at:#f put:(obj adder: 1)!
BlockClosure new "<0x20371a0>"
st> f value:2 !
3
st> [:x :y | x + y] value: 3 value: 4 !
7

なんか value: メッセージの使い方を見ると、一つ目を使った時点で部分適用されたブロックが返ってきてるような気もするんだけど…

st> [:x :y | x + y] value: 3 !
Object: BlockClosure new "<0x2037d40>" error: wrong number of arguments
SystemExceptions.WrongArgumentCount(Exception)>>#signal
SystemExceptions.WrongArgumentCount class(Exception class)>>#signal
BlockClosure>>#value:
UndefinedObject>>#executeStatements
nil

そういうわけではないようだ。ちょっと残念。 ここで使っている Smalltalk というのはシステムワイドな辞書で、こいつにシンボル (頭に # が付いてるのがそう) をキーに値を登録してやることでグローバル変数みたいな使い方ができるみたい。

メソッドに特定の値を返させたい場合は "^" を使って指定する。 逆に言うと、^ を使わないと自動的に self が返るようになってる。 なんか Python っぽい (Python で返ってくるのは None だけど)。

それはそれとして、やっぱ Ruby に似てるなあと思うね。 字面は結構違うけど、やってることは Ruby そっくり。

% irb
irb(main):001:0> class Object
irb(main):002:1>   def adder(x)
irb(main):003:2>     Proc.new {|y| x + y}
irb(main):004:2>   end
irb(main):005:1> end
=> nil
irb(main):006:0> obj = Object.new
=> #<Object:0x5cbc0>
irb(main):007:0> f = obj.adder(1)
=> #<Proc:0x000600cc@(irb):3>
irb(main):008:0> f.call(2)
=> 3

ね。 sumim さんとこでも結構 Smalltalk で書いて Ruby に直訳ってことをやってるけど、なんかすごく納得って感じ。 逆に Objective-C なんかは字面は結構似てる (メッセージ式なんかは、わりとまんまだ) けど、同じようなことやろうとするとかなり違った感じになるはず。 コードブロックにあたるものが無いし (メッセージ式に角括弧使ってる時点でコードブロックを採用する気が無かったのがバレバレ)。 そういや Objective-C 2.0 でクロージャがサポートされるとかって話はどうなったんだっけな?

% [Smalltalk] 配列は 1-origin

これは気を付けないと。 まあ、イテレータがあるからインデックスに気をつける場面って少なそうではあるが。

st> #(1 2 3 4 5) at:1!
1
st> #(1 2 3 4 5) at:5!
5
st> #(1 2 3 4 5) at:0!
Object: Array new: 5 "<0x2043f18>" error: Invalid index 0: index out of range
SystemExceptions.IndexOutOfRange(Exception)>>#signal
SystemExceptions.IndexOutOfRange class>>#signalOn:withIndex:
Array(Object)>>#checkIndexableBounds:
Array(Object)>>#at:
UndefinedObject>>#executeStatements
nil

% [Smalltalk][雑談] 先生、どうしても printNl を println って書いてしまいます

たくさんの言語に手当たり次第手を出すことの弊害がここに(笑

つーか println って何の言語だっけ。Java か? System.out.println か。

OCaml は print_endline だな。これは忘れないぞ。 Haskell は putStrLn だっけ。 Ruby は puts か? まあ p を使っちゃうこと多いけど。 Python は print、Scheme も print か? Scala は Java の影響か Console.println だな。 Nemerle はまんま .Net だから System.Console.WriteLine とか。 Erlang にはたしか改行付きの出力は無かった気がする。

なんかすごくどうでもいー豆知識を披露してしまった……

% [Smalltalk][Python] セミコロンの扱い

なんかごちゃごちゃ色んなページ見ながらやってたら、どこに書いてあったのかわかんなくなっちゃったんだけど、Smalltalk におけるセミコロンって単なる式の区切りじゃないんだね。

st> !Object methodsFor: ''!
Object
st> add: x and: y
st>   ^ x + y
st> !!
st> Smalltalk at:#obj put:(Object new)!
Object new "<0x2038cf0>"
st> obj add: 1 and: 2!
3
st> (obj add: 1 and: 2) printNl!
3
3
st> obj add: 1 and: 2; printNl!
an Object
Object new "<0x2038cf0>"

要するに、セミコロンの前で使われてるのと同じレシーバーにメッセージを投げるという動作をするらしい。

これだ。Python に今必要なのはこれですよ。 どんな記号使うかは別として、同じオブジェクトに連続してメッセージを送る文法があれば、いちいち None を返されて悲しい思いをすることもなくなるよ。 例えば $ を使うとして、

ary = [1,2,3,4]
ary.append(5); $.append(6)

こんなとかさ。 あー、でも生粋の Pythonista からはブーイングが出そうかもなー。 わしは結構欲しいけど。 記号は $ じゃない方が良いかな。$ だと Perl を思い出すから(爆

% [Smalltalk] 文法は最初にここを見るべきだった...orz

ここ→ A Simple Overview of Smalltalk Syntax.

% [Smalltalk] Class reference とか見ながら模索、Namespace 編

Namespace とか RootNamespace とか見つつ。

st> Namespace current name !
#Smalltalk
st> Smalltalk addSubspace: #Hoge !
Namespace new: 32 "<0x20369e0>"
st> Namespace current: Hoge !
Namespace
st> Object subclass: #Fuga
st>   instanceVariableNames: ''
st>   classVariableNames: ''
st>   poolDictionaries: ''
st>   category: nil
st> !
Fuga
st> Fuga new printNl !
a Fuga
Fuga new "<0x2038338>"
st> Namespace current: Smalltalk !
Namespace
st> Fuga new printNl !
stdin:12: undefined variable Fuga referenced
st> #{Hoge.Fuga} value new printNl !
a Fuga
Fuga new "<0x2039ae0>"
  1. Smalltalk というのがグローバルなネームスペースで、
  2. そこに Hoge という名前のサブネームスペースを作って、
  3. カレントネームスペースを Hoge に切り替えて、
  4. Fuga というクラスを作って、
  5. カレントが Hoge のときは単に Fuga でアクセスできて、
  6. カレントを Smalltalk に切り替えると単に Fuga ではアクセスできなくて、
  7. #{Hoge.Fuga} value で Hoge ネームスペース上の Fuga にアクセスできた

というような流れ。#{Hoge.Fuga} value というのは、さっきの文法のページに載ってた binding 式とかいうもの。 こうじゃなくて↓のようにも書けるが、ネームスペースのネストが深くなると、たぶん binding 式の方が簡潔に書ける。

st> (Hoge at: #Fuga) new printNl !
a Fuga
Fuga new "<0x203b090>"

漠然と、この Namespace って仕組みは Gauche のモジュールに似てるかなという気がする。 基本的に親ネームスペースの環境を引き継ぐので、上書きしないかぎり親と同じものが見えるとか (上の例で言えば Object を何も考えずに使えてるあたりがそう)、カレントネームスペースを切り替えられるとか。

% [Smalltalk] category って何のためにあるんだろ?

正直さっぱりわからない。 クラスオブジェクトに category ってメッセージを送るとそのクラスのカテゴリが返ってくるんだけど、だから何? って感じだし。 一瞬 Objective-C のカテゴリを思い浮かべたけど、全然違うものっぽい。

まあ、comment メッセージなんてのもあるし、単にクラスブラウザとかで見るときに分類に使われるとかその程度なのかもしれないが……

本日のツッコミ(全4件) [ツッコミを入れる]
% 向井 (2007-02-02 14:44)

OCamlのラベル引数は、ラベル名と同じ変数名のときの簡略記法がありますけど、むしろあっちがメインなんだと思うのです。何かのサンプルがあっちを積極的に使って書いてあって、そのように思うようになりました。<br># let f ~x ~y = x - y;;<br>val f : x:int -> y:int -> int = <fun><br># let (x, y) = (1, 2);;<br>val x : int = 1<br>val y : int = 2<br># f ~x ~y;;<br>- : int = -1<br># f ~y ~x;;<br>- : int = -1<br><br>これはこれで美しいと思うのですよ。<br>そうでないときはもうほんとキモいんですが(というかラベル指定と型指定が同じ記号なのでうっかりミスでパースエラーしまくり)。

% jijixi (2007-02-02 14:59)

こんな使い方全然知らなかったです(苦笑<br>うまく使えばキレイなコードが書けそうな気がしますね。

% sumim (2007-02-03 00:13)

カテゴリーには、クラスを整理するためのクラスカテゴリーと、メソッドを整理するためのメッセージカテゴリがあります(後者はプロトコルと呼ぶこともありますが、やはり Objective-C のそれとは違います…)。いずれも Smalltalk では多くなりがちなクラスやメソッドを分類して扱いやすくするためのメタ情報です。お察しのとおり GUI を排除して使用する GNU Smalltalk ではほとんど役に立たない名残りのようなものでもあります。強いていうならば、後者については Dictionary fileOutCategory: 'accessing' to: 'dic-acc.st'! などというように、カテゴリを限って興味のあるクラスの興味のあるメソッド群のソースをいっきに目を通したいときなどに使えるかもしれません。そうだとしても、GNU Smalltalk では(たとえば、Squeak なら #allMethodsInCategory: といったような、ユーティリティメソッドが整備されておらず…)あまり使い出のない情報のように個人的には感じました。

% jijixi (2007-02-03 09:06)

なるほど。<br>いずれ Squeak をいじったりすることもあるかも知れないので、そのときのために憶えておくことにします。

お名前:
E-mail:
コメント:

トップ «前の日記(2007-02-01) 最新 次の日記(2007-02-03)» 編集

日記ってのは本来、自分で読み返すためにあるもんだよなあ……
もしくは有名人になったら死後に本になったりとかか?

RSS はこちら

jijixi at azito.com