読者です 読者をやめる 読者になる 読者になる

年金ロボットをめざして

FX(外国為替証拠金取引)自動運用ロボットの開発ブログのはずだった

プログラミング言語 Io

Io言語(以下単にIo)は大変魅力的なプログラミング言語だが日本語による情報がほとんどない。そこで、過去ぼくが書いた Io についての記事をここの掲載しておこうと思う。これを書いた時期にはこの Io と Haskell を勉強していたが、現在はどちらも封印している。ウェブとロボットに集中するためにぼくの趣味の一つであるプログラミング言語についての探求を棚に上げたというわけだ。しかしせっかく書いた記事でもあり、Io に興味のある人たちの参考になるとも思いここに再掲載することにしたのだ。

【 Io Language - http://iolanguage.org/
f:id:fxrobot:20131011230149j:plain

Io は JavaScript のごとくプロトタイプベースで Smalltalk のごとく純粋オブジェクト指向の言語。誕生したのは 2002年4月とのこと。現在満11歳の言語だ。この Io はメッセージ・パッシングという単一機構にこだわった単純明快な言語のようだ。高階関数をもち再帰が可能で、並行処理のためのコルーチン、アクター、フューチャをもつ。また、ガベージコレクションも備えている。

ところで、Bruce A. Tate という人の書いた本「Seven Languages In Seven Weeks」で、7つの言語:Ruby、Io、Prolog、Scala、ErlangClojureHaskell が取り上げられている。Io と Prolog を除く他の5つは現在最も注目される言語たちといってよいであろう。これらに比べると特に Io の知名度はかなり低いのではないか。しかし、Io は他の6つにに負けないほど魅力的かもしれない。少なくともぼくは他の言語以上に注目しているのだ。

どんなゲームが好きかで言語の好も想像できる。お膳立てやイベントがたくさんあって、それに沿っていけばエンディングにたどり着き、そこでボスキャラと一騎打ち。それも悪くはないだろう。しかし、ぼくは小さなルールだけがあって多くの村や町を自由に行き来するゲームが好きだ。言語でいえば、それは Io ということになる。

最近、Io の公式サイト(http://iolanguage.com/)をよく見ているが、全体的にドキュメントが不親切だ。英文でいいから(当たり前か)リファレンスなどの解説を増やしてもらいたいものだ。Io の英語版の書籍がないものかと探したが見当たらない。出る予定もないんだろうなぁ、ソースコードを見ればいい? ぼくにそんな根性はない。まぁ、でも少しずつ分かってきたので、今回、少し紹介しようと思った次第。(ぼくの Io 学習は 2012年5月20日の日曜日に始まった)

この言語は、コマンドプロンプトであれこれ実験するのがとても楽しい。まるで新しいゲームを手に入れてそれにハマってしまったような状態なのだ。今回はその結果を紹介したいと思っているのだが、ぼく自身 Io の初心者であり、間違ったことをだいぶ書いてしまうかもしれない。あらかじめご了承いただき、間違っている点はご教授などいただければ幸いです。

さて、まずは公式サイトから Windows版を手に入れよう。Ubuntu の人も Windows版がおすすめだ(ま、冗談)。ぼくの場合は、C:\Io に放り込んで、C:\Io\bin にパスを通した。ただそれだけ。

Io のためのエディタとしては、現在、Mery を使っているが、jEdithttp://www.jedit.org/)というエディタが Io の構文に対応しているということでインストールしてみた。

f:id:fxrobot:20131011222010j:plain

で、こんな感じで、カラー表示。ちょっと古くて対応していないキーワードもある模様。このくらいなら自前で作ってもいいかも。興味がわいた人は使ってみてください。さてさて、始めよう。まずはシェル。コマンドプロンプトから io と打ち込めばシェルが立ち上がる。

C:\>cd io
C:\Io>io
Io 20110905
Io>

すると、Io のバージョンを表示して入力待ちとなる。おきまりの Hello World をやってみよう。

Io>"Hello World" print
Hello World==> Hello World
Io>exit
C:\Io>

最初の Hello world は print の結果。次の Hello World は結果の値。
exit で抜ける。シェルの話はこれで終わり。いろいろ自由にやってみてほしい。

おっと、ここで、使用する前に Io のコメントについて。次の通り3種類ある。# は先頭行で用いれば Unix 世界の意味を持つ。(詳細は省略)

// コメント
# コメント
/* コメント */

プログラムファイルのエンコードは、日本語が使いたければ UTF-8N(BOM なし)がよいだろう。それで次のようにちょっと試してみたのだ。(右端が切れてしまったがスクロールが可能)

"" println
"// ダメ文字といわれる漢字を表示してみる" println
"// ―ソЫ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭" println
s := "―ソЫ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭"
s println
"\n" println

"// その encoding を表示してみる" println
"―ソЫ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭" encoding println
"\n" println

"// 先頭に a を挿入してみる" println
"a―ソЫ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭" println
"\n" println

"// ヒアドキュメントで先頭に改行を挿入してみる" println
h := """
―ソЫ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭
―ソЫ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭
―ソЫ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭
""" println
"\n" println

"// UTF-8 を指定する" println
u := "―ソЫ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭" asUTF8
u  println
"\n"   println			
"// その encoding は" println
u encoding println
"\n" println

"// 指定しない場合の interpolate" println
"#{s}" interpolate println
"\n" println

"// UTF8 の場合の interpolate" println
"#{u}" interpolate println
"\n" println

結果は次の通り。

f:id:fxrobot:20131011221919j:plain

コマンドプロンプトのコードページはご覧のように 65001 に設定して行った。結果は、日本語文字列の1文字目が化けてしまう。1文字目に半角(スペースや改行もOK)を挿入すると正常となる。まぁ、これは何とか対処できそうだ。受け入れるとしよう。これ、最初からおもしろくない話をしていると思うので、この辺りのこと、あとはお任せする。

さて、それでは、ありがちな普通の話に戻るとしよう。

Io言語は、シーケンス、リスト、マップなどのコレクションとそれぞれに豊富なメソッドをもっている。でも、それを一々書くのは退屈なので次の機会にでも譲りたい。でも、制御構造については紹介したい思う。

//
// Io の Control Flow
//

if(y < 10,
	x := y,
	x := 0
)

if(y < 10) then(x := y) else(x := 0)  // こちらの形式も可

switch(
	n==1, write("n=1"),
	n==2, write("n=2"),
	n==3, write("n=3")
)

loop(
	write("Endless")
)

100 repeat(
	write("100 Times")
)

a := 1
while(a < 10, 
	a println
	a = a + 1
)

for(i, 0, 10, 1,
	i println
)

list("abc", "def", "ghi") foreach(println)  // List の foreach

aMap foreach(k, v, writeln(k, " = ", v))    // Map の foreach

以上、見たとおりだが、switch はぼくが作ったものだ。次のように定義した。

//
// Io に新たな制御構造 switch を自前で定義
//
switch := method(
	ac := call message argCount
	for(i, 0, ac-1, 2,
		(call sender doMessage(call message argAt(i))) ifTrue(
			call sender doMessage(call message argAt(i+1)) break)
	)
)
//
// switch のテスト
//
n := 4
switch(
	n==1, writeln("n=1"),
	n==2, writeln("n=2"),
	n==3, writeln("n=3"),
	n==4, writeln("n=4"), /* ここで一致、n=4 を表示して break する  */
	n==4, writeln("n=4"), /* この行も同じ条件を満たすが実行されない */
	n==5, writeln("n=5")
)
# 実行結果 ==> n=4

次は Io のオブジェクト指向について。Io のオブジェクト指向JavaScript どうようにプロトタイプベースだ。クラスは存在しない。

//
// オブジェクト
//
Account := Object clone // 口座オブジェクトを生成(複製)

Account balance := 0    // 残高スロットの初期化

Account deposit := method(v, balance := balance + v)   // 入金
Account withdraw := method(v, balance := balance - v)  // 出金
Account show := method(
	write("Account balance: ", balance, "\n")      /* 残高表示 */
)

myAccount := Account clone // 自分専用口座オブジェクトを生成

myAccount deposit(10000)    // 10,000円入金
myAccount show              // 残高の確認

上の Object はルートレベルのオブジェクト。

//
// オブジェクトのタイプ
//
Pen := Object clone
BallpointPen := Pen clone
myBallpointPen := BallpointPen clone

Pen type               #==> Pen
BallpointPen type      #==> BallpointPen
myBallpointPen type    #==> BallpointPen

上の例のように小文字で始まるスロットは親プロトタイプのタイプをもつ。

//
// Singleton
//
# MyObject の clone は不可となる

MyObject := Object clone
MyObject clone := method(return self)

上は、MyObject を clone しようとしても自分自身を返すため、複製されることのないオブジェクトとなる。

//
// Overriding
//
Person := Object clone
Person name := "Slump"
Person title := method(writeln(name))

Doctor := Person clone
Doctor title := method(write("Dr. ") resend)

Doctor title println	#==> Dr. Slump

上は、resend によって Doctor の親オブジェクト Person にメッセージを送ることで、Person のコンテキストを使用している。

Io は再帰も可能だ。今どきじゃ、当たりまえか。

//
// 再帰
//
factorial := method(n,
	if (n == 1,
		return 1,
		return n * factorial(n - 1)
	)
)

factorial(10) println  #==> 3628800

次はフィボナッチ数列をやってみよう

//
// フィボナッチ数列
//
fib := method(i, if(i < 3, 1, fib(i -2) + fib(i - 1)))
//
// fib(1) から fib(35) まで表示する
//
for(n, 1, 35, 
    "fib(#{n}):" interpolate alignLeft(9, " ") print
    fib(n) println
)

次はコルーチンを見てみよう。

//
// Coroutine
//
fib := method(i, if(i < 3, 1, fib(i -2) + fib(i - 1)))

o1 := Object clone
o1 fibo1 := method(m, n,
	for(i, m, n, 1
		"o1 fib(#{i}):" interpolate alignLeft(12, " ") print
		fib(i) println; yield
	)
)

o2 := Object clone
o2 fibo2 := method(m, n,
	for(i, m, n, 1
		"o2 fib(#{i}):" interpolate alignLeft(12, " ") print
		fib(i) println; yield
	)
)

o1 @@fibo1(1, 30)
o2 @@fibo2(1, 30)

Coroutine currentCoroutine pause

上の例が平行性として意味のあるプログラムになっているかという点についてはご容赦願いたい。見かけ上は協調して動いているような結果にはなった。一応解説しよう。yield はこれに出会うたびにもう一つのプロセスに制御を移す。また @@fibo1 のように @@ を付けることで非同期なメッセージ送信となる。

似ているようだがもう一つ。Io のもつフューチャについてだ。

//
// future
//
fib := method(i, if(i < 3, 1, fib(i -2) + fib(i - 1)))

futurefib := @fib(31)  // ここで fib(31) を @ 付きで呼び出している

for(n, 1, 30,          /* ここで fib(1)~fib(30) までを計算している */
    "fib(#{n}):" interpolate alignLeft(9, " ") print
    fib(n) println
)

"fib(31): " print; futurefib println  // futurefib にアクセス

前の例では @@ だったが、こんどは @ を用いている。同じ非同期送信ではあるが、@@ はすぐに nil を返すが @ はフューチャを返す。フューチャは自身のプロセスが終了した時点で本来の値を返す。 もし、本来の値が返される前にフューチャにアクセスした場合は、返されるまでブロックされる。

次にファイルの入出力をやってみた。

//
// file1
//
fio := File with("in.txt") openForReading
	allin := fio contents
fio close

allout := allin

foo := File with("out.txt") openForUpdating
	foo write(allout)
foo close

上は、あるテキストファイルを全部を読み込んで、別ファイルに全部そのまま書き込むテストだ。やってみると出力ファイルに改行が2重に書き込まれてしまう。改行コードの問題だろう。試しに入力ファイルの改行コードを LF にしたらうまくいった。うん、うまくはないか。で、次。

//
// file2
//
fio := File with("in.txt") openForReading
	allinList := fio readLines
fio close

allin := allinList join("\n")

allout := allin

foo := File with("out.txt") openForUpdating
	foo write(allout)
foo close

上だが、今度は1行ずつ読み込みリストに格納。それをつないでから書き込んだ。これならと思いテストしたが、結果はうまくいった。うーん、簡単な例ではあるが、ぼくに何か見落としがあるのか..もとは Unix だからね。まぁ、いいか。

シーケンスとリスト、マップをサボったけど、ずいぶん長く書いてしまった。今回はコマンドの引数について書いて最後にしよう。

//
// args.io 引数を受け取るテストプログラム
//
myargs := System args println
for(i, 0, 3, 1, myargs at(i) println)

// コマンドプロンプトから
// io args.io AAAAA BBBBB CCCCC
// の実行結果
#==> list(args.io, AAAAA, BBBBB, CCCCC)
#==> args.io
#==> AAAAA
#==> BBBBB
#==> CCCCC

いえね、これ、簡単なことだけど、ぼくはすぐに気づかなかった。そういうわけで、ここで、ぼくどうようの「気づかない系」の人のためにお教えしようと思った次第。

さてさて、Io は文字列処理にも優れるように思う。リストやマップにも必要以上のメソッドがそろっている。それらについて今回は割愛したが、それらが気に入ったからこそ Io をもう少し使ってみようと思う。

ぼくにとって Io言語はおもしろくて神秘的だ。っていうことでも分かるように、ぼくには Io言語がまだよく分からない。もちろん、ここまで程度の理解で小さなプログラムなら実用的なものを書くこともできる。文字列処理やリスト処理辺りなら自由自在だ。この辺りの事情はすべてを理解しなくても使える JavaScript とどうようだ。しかし、Io言語の原理をすべて完全に理解したい。その辺りが一番おもしろいように思う。さて、ここからも思いついたまま気が向いたままに書きたいと思う。Io言語の学習を初めて10日ほどの初心者が書くことなので内容に関しては大目に見てほしい。

f:id:fxrobot:20131011232648j:plain

Io言語ではすべてのものがオブジェクトであり、すべてのアクションはメッセージなのだ。
これが Io言語がおもしろい一つの理由だ。

さて、見てのとおり Io のオブジェクトを作るのは簡単だ。

GameMachine :=	Object clone
PlayStation :=	GameMachine clone

上では GameMachineオブジェクトをルートオブジェクトである Object を複製することで生成している。次に GameMachine を親にもつ PlayStation を生成。どうようにして次々と複製していく。

また、あるオブジェクトの protosリストには、proto をいくつでも追加でき、これによって多重継承を実現している。

myObject appendProto(otherObject)

次に protos と proto についてもう少ししらべてみよう。

f:id:fxrobot:20131011232704j:plain

上の図は Io言語のネームスペースについてのものだ。番号付き矢印を追っかけてみてほしい。

Lobby というのはこの図でも分かるようにネームスペースの元締めのような存在だ。また、図ではすべてのオブジェクトはプロトタイプへのリンクをもっており、自分の知らないメッセージを受けたときは protoリストを深さ優先でたどっていく。protos には proto の全リストが入っており、次のようにして知ることができる。

o := Object clone
p := o protos
p print

あるオブジェクトの protos の最初のリストが直前の親 proto のスロットリストになっているようだ。ルートオブジェクトである Object の場合は その protos と proto は同一のリストとなる。この辺りの表現は正確性を欠いているかもしれない。

次のリストはルートオブジェクトの protos を print した結果だ。つまり、すべてのオブジェクトは最低限これらのスロットが使用できるということになる。

//
// protos
//

o := Object clone
p := o protos
p print

#==↓ Object にはこれだけのスロットがある
/*
list( Object_0x3a4070:
                   = Object_()
  !=               = Object_!=()
  -                = Object_-()
  ..               = method(arg, ...)
  <                = Object_<()
  <=               = Object_<=()
  ==               = Object_==()
  >                = Object_>()
  >=               = Object_>=()
  ?                = method(...)
  @                = method(...)
  @@               = method(...)
  actorProcessQueue = method(...)
  actorRun         = method(...)
  addTrait         = method(obj, ...)
  ancestorWithSlot = Object_ancestorWithSlot()
  ancestors        = method(a, ...)
  and              = method(v, ...)
  appendProto      = Object_appendProto()
  apropos          = method(keyword, ...)
  argIsActivationRecord = Object_argIsActivationRecord()
  argIsCall        = Object_argIsCall()
  asSimpleString   = method(...)
  asString         = method(keyword, ...)
  asyncSend        = method(...)
  become           = Object_become()
  block            = Object_block()
  break            = Object_break()
  clone            = Object_clone()
  cloneWithoutInit = Object_cloneWithoutInit()
  compare          = Object_compare()
  contextWithSlot  = Object_contextWithSlot()
  continue         = Object_continue()
  coroDo           = method(...)
  coroDoLater      = method(...)
  coroFor          = method(...)
  coroWith         = method(...)
  currentCoro      = method(...)
  deprecatedWarning = method(newName, ...)
  do               = Object_do()
  doFile           = Object_doFile()
  doMessage        = Object_doMessage()
  doRelativeFile   = method(path, ...)
  doString         = Object_doString()
  evalArg          = Object_evalArg()
  evalArgAndReturnNil = Object_evalArgAndReturnNil()
  evalArgAndReturnSelf = Object_evalArgAndReturnSelf()
  for              = Object_for()
  foreachSlot      = method(...)
  futureSend       = method(...)
  getLocalSlot     = Object_getLocalSlot()
  getSlot          = Object_getSlot()
  handleActorException = method(e, ...)
  hasDirtySlot     = Object_hasDirtySlot()
  hasLocalSlot     = Object_hasLocalSlot()
  hasProto         = Object_hasProto()
  hasSlot          = method(n, ...)
  if               = Object_if()
  ifError          = method(...)
  ifNil            = Object_thisContext()
  ifNilEval        = Object_thisContext()
  ifNonNil         = Object_evalArgAndReturnSelf()
  ifNonNilEval     = Object_evalArg()
  in               = method(aList, ...)
  init             = Object_init()
  inlineMethod     = method(...)
  isActivatable    = Object_isActivatable()
  isError          = false
  isIdenticalTo    = Object_isIdenticalTo()
  isKindOf         = method(anObject, ...)
  isLaunchScript   = method(...)
  isNil            = false
  isTrue           = true
  justSerialized   = method(stream, ...)
  launchFile       = method(path, args, ...)
  lazySlot         = method(...)
  lexicalDo        = Object_lexicalDo()
  list             = method(...)
  loop             = Object_loop()
  markClean        = Object_markClean()
  memorySize       = Object_memorySize()
  message          = Object_message()
  method           = Object_method()
  newSlot          = method(name, value, doc, ...)
  not              = nil
  or               = true
  ownsSlots        = Object_ownsSlots()
  pause            = method(...)
  perform          = Object_perform()
  performWithArgList = Object_performWithArgList()
  prependProto     = Object_prependProto()
  print            = method(...)
  println          = method(...)
  proto            = Object_proto()
  protos           = Object_protos()
  raiseIfError     = method(...)
  relativeDoFile   = method(path, ...)
  removeAllProtos  = Object_removeAllProtos()
  removeAllSlots   = Object_removeAllSlots()
  removeProto      = Object_removeProto()
  removeSlot       = Object_removeSlot()
  resend           = method(...)
  return           = Object_return()
  returnIfError    = method(...)
  returnIfNonNil   = Object_returnIfNonNil()
  serialized       = method(stream, ...)
  serializedSlots  = method(stream, ...)
  serializedSlotsWithNames = method(names, stream, ...)
  setIsActivatable = Object_setIsActivatable()
  setProto         = Object_setProto()
  setProtos        = Object_setProtos()
  setSlot          = Object_setSlot()
  setSlotWithType  = Object_setSlotWithType()
  shallowCopy      = Object_shallowCopy()
  slotDescriptionMap = method(...)
  slotNames        = Object_slotNames()
  slotSummary      = method(keyword, ...)
  slotValues       = Object_slotValues()
  stopStatus       = Object_stopStatus()
  super            = method(...)
  switch           = method(...)
  thisContext      = Object_thisContext()
  thisLocalContext = Object_thisLocalContext()
  thisMessage      = Object_thisMessage()
  try              = method(...)
  type             = Object_type()
  uniqueHexId      = method(...)
  uniqueId         = Object_uniqueId()
  updateSlot       = Object_updateSlot()
  wait             = method(s, ...)
  while            = Object_while()
  write            = Object_write()
  writeln          = Object_writeln()
  yield            = method(...)
)
*/

次に init、forward などをしらべてみよう。

init はコンストラクタに相当する特別なメソッドで、当該オブジェクトが複製されたときに実行される。次のリストで確認できるだろう。また、同じく次のリストで forward の挙動も確認できる。リストにある例では myMemo オブジェクトに hogehoge という存在しないメッセージを送ったとき forwardメソッドが起動されているが、もちろん、forward はそのようなためだけに存在するはずはなく、メッセージに応じて他のオブジェクトにメッセージを(分析、加工して)正に「転送」するためのものではないだろうか。

//
// slot
//

Memo := Object clone do(
	ownerName	:= "Uninitialized"
	ownerPhone	:= "Uninitialized"
	ownerAddress 	:= "Uninitialized"
	ownerMemo	:= "Uninitialized"
	init := method(		/* init */
		self ownerName		:= "Owner's Name"
		self ownerPhone		:= "Owner's Phone"
		self ownerAddress	:= "Owner's Address"
		self ownerMemo		:= "Owner's Memo"
	)
	forward := method(	/* forward */
		"I don't know it !" println
	)
)

Memo ownerName		println	#==> Uninitialized
Memo ownerPhone		println	#==> Uninitialized
Memo ownerAddress	println	#==> Uninitialized
Memo ownerMemo		println	#==> Uninitialized

myMemo := Memo clone
myMemo ownerName	println	#==> Owner's Name
myMemo ownerPhone	println	#==> Owner's Phone
myMemo ownerAddress  	println	#==> Owner's Address
myMemo ownerMemo	println	#==> Owner's Memo

myMemo hogehoge			#==> I don't know it !

myMemo slotNames	println
#==> list(ownerMemo, ownerName, ownerPhone, ownerAddress)

Memo slotNames		println
#==> list(init, type, ownerPhone, ownerAddress, ownerMemo, ownerName)

slotNames	println
#==> list(Lobby, Protos, set_, exit, Memo, myMemo, _, forward)

slotSummary	println
#==> Object_0x4675c0:
#==> Lobby		= Object_0x4675c0
#==> Memo		= Memo_0x215b450
#==> Protos		= Object_0x467560
#==> _			= nil
#==> exit		= method(...)
#==> forward		= method(...)
#==> myMemo		= Memo_0x215bae0
#==> set_		= methjod(...)

また、上では slotNames と slotSummary についてもテストしているのでその結果を確認してほしい。

次はマップについてだ。

Io言語のマップは特定のリテラル形式がないようなのだ。atPut によって追加していくか with を用いる。あるいは、list のリテラル形式を使って作成して、あとでマップとして利用する方法もあると思うが、そのときの効率の問題については分からない。また、マップの内容を表示する場合は asList を用いてリストデータとして表示する。(右端が切れてしまったがスクロールが可能)

//
// Map
//

m := Map clone		// Mapオブジェクトの生成
m atPut("aaa", "AAA")	// atPutでMapデータを追加していく
m atPut("bbb", "BBB")
m atPut("ccc", "CCC")

m println		#==> Map_0x204ac70:
m type println		#==> Map
m asList println	#==> list(list(ccc, CCC), list(aaa, AAA), list(bbb, BBB))

m keys println		#==> list(ccc, bbb, aaa)
m values println	#==> list(CCC, BBB, AAA)
m size println		#==> 3

m hasKey("aaa") println		#==> true
m hasKey("ddd") println		#==> false
m hasValue("AAA") println	#==> true
m hasValue("DDD") println	#==> false

m at("aaa") println	#==> AAA
m at("bbb") println	#==> BBB
m at("ccc") println	#==> CCC

n := Map clone		// Mapオブジェクトの生成
n atPut("ddd", "DDD")
m = m merge(n)		// Map m に Map nをマージ
m asList println	#==> list(list(ccc, CCC), list(aaa, AAA), list(ddd, DDD), list(bbb, BBB))

m removeAt("ddd")	// Map m の項目の削除
m asList println	#==>  list(list(ccc, CCC), list(aaa, AAA), list(bbb, BBB))

m empty			// Map m のデータを全削除
m asList println	#==> list()
m println		#==> Map_0x1fdf260  空のMap

m = m with(		/* withでMapデータを作成 */
	"xxx", "XXX",
	"yyy", "YYY",
	"zzz", "ZZZ"
	)
m asList println	#==> list(list(zzz, ZZZ), list(yyy, YYY), list(xxx, XXX))

l := List clone		// Listオブジェクトの生成
l = list(		/* Map候補をListリテラルで作成 */
	list("aaa", "AAA"),
	list("bbb", "BBB"),
	list("ccc", "CCC")
	)

l println		#==> list(list(aaa, AAA), list(bbb, BBB), list(ccc, CCC))
l type println		#==> List
lMap := l asMap
lMap println		#==> Map_0x204d730:
lMap asList println	#==> list(list(ccc, CCC), list(aaa, AAA), list(bbb, BBB))

今回は以上とする。
Io について書けるまたの機会があれば幸いだ。