##Io简介 大多数的Io社区都致力于将Io作为带有微型虚拟机和丰富并发特性的可嵌入语言来推广。
Io的核心优势是拥有大量可定制的语法和函数,以及强有力的并发模型。
在Io中,万事万物皆为消息,且每条消息都灰返回另一接受消息的对象。Io这门语言没有关键字,有的只是少量在行为上接近于关键字的字符。
##对象、原型和继承 Io是一门原型语言,所有的对象都有原型,对象还带有槽(slot,相当于类的域和方法??)。
槽的相关操作
- :=当槽不存在,Io会创建一个槽,然后赋值
- =给槽赋值,如果槽不存在,抛出异常
- ::=新建槽
- type 任何对象都有type这个槽,返回对象的原型
- slotNames返回对象的槽名列表
- getSlot(name)获取槽的内容,如果槽不存在会获取父对象的槽
Io的类型是一个非常好的机制。从惯用的角度说,以大写开开头的对象时类型,因此Io会对它设置type槽。而类型的复制品若以小写开头则会调用它父对象的type槽。
##方法 方法是槽的一种?
方法也是对象,和其它类型的对象一样,你可以获取它的类型。
Io>method() type
==>BlockLobby是主命名空间(相当于浏览器中javascript的window?),包含所有已命名的对象。
Io> Lobby
Protos = Object_0x1dac868
_ = Object_0x1dac8c8
exit = method(...)
forward = method(...)
set_ = method(...)- 所有事物都是对象;
- 所有与对象的交互都是消息
- 你要做的不是实例化类,而是复制那些叫做原型的对象
- 对象会记住它的原型
- 对象有槽
- 槽包含对象(包括方法对象)
- 消息返回槽中的值,或调用槽中的方法
- 如果对象无法响应某消息,它则会把消息发送给自己的原型
###单例 true、false和nil都是单例(singleton),对它们进行复制,返回的只是单例对象的值。构建单例只需重定义clone方法,让它返回单例对象自身即可。
##第一天自习
- 对1+1求值,然后对1+"one"求值。Io是强类型还是弱类型?用代码证实你的答案。
Io> 1+1
==> 2
Io> 1+"one"
Exception: argument 0 to method '+' must be a Number, not a 'Sequence'
---------
message '+' in 'Command Line' on line 1
Io> "1"+"one"
Exception: Io Assertion 'operation not valid on non-number encodings'
---------
message '+' in 'Command Line' on line 1
Io> "1" .. "one"
==> 1one
Io> 1 .. "one"
==> 1one- 0是true还是false?空字符串是true还是false?nil是true还是false?用代码证实你的答案。
Io> if( 0 , true println,false println)
true
==> true
Io> if( "" , true println,false println)
true
==> true
Io> if( nil , true println,false println)
false
==> false- 如何知道某个原型具有哪些槽?
Io> Sequence slotNames foreach(println)
log
linePrint
beforeSeq
pathComponent
urlDecoded
removeSeq
bitwiseAnd
removeOddIndexes
uppercase
findSeq
replaceSeq
cPrint
logicalOr
...- =、:=、::=之间有什么区别?你会在什么时候使用它们?
Io> OperatorTable
==> OperatorTable_0x951180:
Operators
0 ? @ @@
...
Assign Operators
::= newSlot
:= setSlot
= updateSlot实际测试没有发现::=与:=的区别。
- 从文件中运行Io程序。
D:\IoCodes>cat sum.io
sum := 0
for(i,1,100,
sum = sum + i
)
sum println
D:\IoCodes>io sum.io
5050##运算符
Io> OperatorTable addOperator("xor",11)
==> OperatorTable_0x1d61180:
Operators
...
10 && and
11 or xor ||
12 ..
13 %= &= *= += -= /= <<= >>= ^= |=
14 return
Assign Operators
::= newSlot
:= setSlot
= updateSlot
To add a new operator: OperatorTable addOperator("+", 4) and implement the + message.
To add a new assign operator: OperatorTable addAssignOperator("=", "updateSlot") and implement the updateSlot message.
Io> true xor := method(bool,if(bool,false,true))
==> method(bool,
if(bool, false, true)
)
Io> false xor := method(bool,if(bool,true,false))
==> method(bool,
if(bool, true, false)
)
Io> true xor false
==> true##消息 在Io中,几乎一切都是消息。一个消息由三部分组成:发送者(sender)、目标(target)、参数(arguments)。你可以用call方法访问任何消息的元信息。
//msg.io
MsgReceiver := Object clone
MsgReceiver receive := method(
"message name is : " print
call message name println
"sender is :" println
call sender println
"target is :" println
call target println
"arguments is :" println
call message arguments println
"arguments[0] is : " print
call message argAt(0) println
"arguments[0] is : " print
call argAt(0) type println
"arguments[0] is : " print
call evalArgAt(0) type println
)
MsgSender := Object clone
MsgSender send := method(
"message name is : " print
call message name println
"sender is :" println
call sender println
"target is :" println
call target println
receiver := MsgReceiver clone
receiver receive("hollo world!")
)
sender := MsgSender clone
sender send
//result:
message name is : send
sender is :
Object_0x44c890:
Lobby = Object_0x44c890
MsgReceiver = MsgReceiver_0x2171178
MsgSender = MsgSender_0x2171238
Protos = Object_0x44c830
_ = nil
exit = method(...)
forward = method(...)
sender = MsgSender_0x21712c8
set_ = method(...)
target is :
MsgSender_0x21712c8:
message name is : receive
sender is :
MsgSender_0x21712c8:
target is :
MsgReceiver_0x217c368:
arguments is :
list("hollo world!")
arguments[0] is : "hollo world!"
arguments[0] is : Message
arguments[0] is : Sequence
[Finished in 0.2s]可以看到,send方法的发送者是全局对象Lobby。 evalArgAt和argAt是两者间的区别:
evalArgAt(argNumber)
Evaluates the specified argument of the Call’s message in the context of it’s sender.argAt(argNumber)
Returns the message’s argNumber arg. Shorthand for same as call message argAt(argNumber).
大多数语言都将参数作为栈上的值传递,但是Io不是这样。Io传递的是消息本身和上下文,在由接受者对消息求值。实际上你可以用消息实现控制结构。
##反射 在Io中,处理反射分为两个部分。在邮局那个例子中,是消息反射。对象反射是处理对象和对象的槽。
Object ancestors := method(
prototype := self proto
if(prototype !=Object,
writeln("Slots of ",prototype type,"\n---------------------")
prototype slotNames foreach(slotName, writeln(slotName))
writeln
prototype ancestors))
Animal := Object clone
Animal speak := method(
"ambigulous animal noise" println
)
Duck := Animal clone
Duck speak := method(
"quack" println
)
Duck walk := method(
"waddle" println
)
disco := Duck clone
disco ancestors
disco walk
disco speak
***************output:*******************
Slots of Duck
---------------------
walk
speak
type
Slots of Animal
---------------------
speak
type
waddle
quack##第二天自习
- 计算斐波那契数列的递归和循环两种方法:
fib := method(n,
if(n ==1 or n == 2) then(
return 1
) else (
return (fib(n-1) + fib(n-2))
)
)
fib2 := method(n,
if(n == 1) then (
return 1
) else (
prv1 := 1
prv2 := 0
cur := 0
for(i,2,n,
cur = prv1 + prv2
prv2 = prv1
prv1 = cur
)
return cur
)
)- 在分母为0的情况下如何让运算符/返回0?
Number setSlot("coreDivision", Number getSlot("/"))
Number / = method(n,
if(n==0) then(
return 0
)else(
return (self coreDivision(n))
)
)
(4/0) println
(8/4) println
(16/8) println
(16 / -8) println- 写一个程序,把二维数组的所有数相加
Io中没有Array这个原型,所以用List代替数组
sum := method(arr,
if(arr type != "List") then(
return 0
)
sum := 0
arr foreach(e,
if(e type != "List") then(
continue
)
if(e size < 2) then(
continue
)
sum = sum + e at(0)
sum = sum + e at(1)
)
return sum
)
sum(list(list(3,4),list(5))) println
sum(list(7,8)) println
************output*************
7
0- 对列表增加一个名为myAverage的槽,以计算所有数字的平均值。
List myAverage := method(
if(self isEmpty) then( return 0 )
sum := 0
self foreach(e,
if(e type != "Number") then(
Exception raise("List member is not a number")
)
sum = sum + e
)
avg := sum / self size
)
list() myAverage println
list(3,4,5,1.2) myAverage println- 对二维矩阵写一个原型。该原型dim的方法可为一个包含y个列表的列表分配内存,其中每个列表有x个元素,set(x,y) 方法可以设置类别中的值,get(x,y)方法可返回列表中的值。
TwoDArray := Object clone
TwoDArray dim := method(x,y,
self data := list()
for(i,0,x-1,
innerList := list()
for(j,0,y-1,
innerList append(nil)
)
data append(innerList)
)
)
TwoDArray set := method(x,y,e,
outterList := self data
if(x > outterList size, return)
innerList := outterList at(x)
if(y > innerList size, return)
innerList atPut(y,e)
)
TwoDArray get := method(x,y,
outterList := self data
if(x > outterList size, return nil)
innerList := outterList at(x)
if(y > innerList size, return nil)
innerList at(y)
)
arr2 := TwoDArray clone
arr2 dim(3,4)
arr2 set(1,2,3)
arr2 set(1,10,3)
arr2 get(1,2) println
arr2 get(1,10) printlndim(x,y)分配了包含x个列表的列表,每个子列表有y个元素,与题目有点不同,但觉得这样更好理解。 代码没有对数组下标越界抛出异常,抛出异常应该更好。
- 写一个转置方法,是原列表上的matrix get(x,y)与转置后的列表(new_matrix get(y,x))相等。
TwoDArray transpose := method(
outterList := self data
y := outterList size
innerList := outterList at(0)
x := innerList size
ret := TwoDArray clone
ret dim(x, y)
for(i,0,x-1,
for(j,0,y-1,
ret set(i, j, self get(j, i))
)
)
return ret
)
arr2 := TwoDArray clone
arr2 dim(3,4)
arr2 set(1,2,3)
arr2 set(1,10,3)
arr2 get(1,2) println
arr2 get(1,10) println
"-----------" println
arr3 := arr2 transpose
arr3 get(2,1) println
arr3 get(10,1) println- 将文件写入矩阵,并从文件读取矩阵
TwoDArray writeMatrix := method(filePath,
f := File with(filePath)
f remove
f openForUpdating
outterList := self data
x := outterList size
y := outterList at(0) size
f write((x .. "," .. y .. "\n"))
outterList foreach(e,
line := ""
e foreach(e2,
line = line .. e2 .. ","
)
f write(line exSlice(0,-1),"\n")
)
f close
)
TwoDArray readMatrix := method(filePath,
f := File with(filePath)
f openForReading
line := f readLine
d := line split(",")
x := d at(0) asNumber
y := d at(1) asNumber
ret := TwoDArray clone
ret dim(x,y)
writeln("x=",x,", y=",y)
i := 0
j := 0
f readLines foreach(line,
list := line split(",")
j = 0
list foreach(e,
writeln(i,"x",j,"=",e)
if(e != "nil",ret set(i,j,e))
j = j + 1
)
i = i +1
)
f close
return ret
)
TwoDArray writeMatrix2 := method(filePath,
f := File with(filePath)
f remove
f openForUpdating
f write(self serialized())
f close
)
TwoDArray readMatrix2 := method(filePath,
ret := doFile(filePath)
return ret
)
arr2 := TwoDArray clone
arr2 dim(3,4)
arr2 set(1,2,3)
arr2 set(1,10,3)
arr2 get(1,2) println
arr2 get(1,10) println
"-----------" println
arr3 := arr2 transpose
arr3 get(2,1) println
arr3 get(10,1) println
arr3 writeMatrix("./test.dt")
arr4 := TwoDArray readMatrix("./test.dt")
arr4 println
arr4 get(2,1) println
arr4 get(10,1) println
arr3 writeMatrix2("./test2.dt")
arr5 := TwoDArray readMatrix2("./test2.dt")
arr5 println
arr5 get(2,1) println
arr5 get(10,1) println- 写一个程序,提供10次尝试机会,猜一个1~100之间的随机数。如果
GuessNumGame := Object clone
GuessNumGame init := method(
self secretNum := Random value(1,101) floor
self guessTimes := 0
self lowBoundry := 0
self highBoundry := 100
secretNum println
)
GuessNumGame guess := method(x,
guessTimes println
if(guessTimes > 10) then(
"You had guess over 10 times! Game over!" println
return
)
if(x < secretNum) then(
if(x > lowBoundry,lowBoundry = x)
writeln("You guess lowwer. You can guess between ",lowBoundry," to ",highBoundry)
) elseif(x == self secretNum) then(
writeln("Congratulations! You got the right number!")
) else(
if(x < highBoundry, highBoundry = x)
writeln("You guess higher. You can guess between ",lowBoundry," to ",highBoundry)
)
guessTimes = guessTimes + 1
)
game := GuessNumGame clone
game guess(10)
game guess(50)##领域特定语言 几乎每一个研究过Io语言的人,都会对它在DSL方面的强大赞不绝口。下面实现一种有趣的电话号码语法的API。
比如:
{
"Bob smith":"5195551212",
"Mary Walsh":"4162223434"
}解决这一个问题的办法:
OperatorTable addAssignOperator(":","atPutNumber")
curlyBrackets := method(
r := Map clone
call message arguments foreach(arg,
r doMessage(arg)
)
r
)
Map atPutNumber := method(
self atPut(
call evalArgAt(0) asMutable removePrefix("\"") removeSuffix("\""),
call evalArgAt(1)
)
)
s := File with("phonebook.txt") openForReading contents
phoneNumbers := doString(s) //doString把电话号码簿求值为Io代码
phoneNumbers keys println
phoneNumbers values println##Io的method_messing 就像Ruby的method_missing那样,你也可以用Io的forward消息做到同样的事,但是这样做的风险会高一些。Io没有类,所以改变forward也将改变从Object获得的基本行为方式。
XML是对数据进行结构化的绝妙方式,但是却有着令人作呕的语法。为了摆脱这语法,你可以写一个程序,用Io代码来表示XML数据。 假如你想把下面的数据:
<body>
<p>
This is a simple paragraph.
</p>
</body>表示成:
body(
p("This is a simple paragraph.")
)我们把这种新语言称作LispML。我们将用Io的forward处理这门语言,就像处理不存在的方法一样(missing_method)。
Builder := Object clone
Builder forward := method(
writeln("<",call message name,">")
call message arguments foreach(arg,
content := self doMessage(arg)
if(content type == "Sequence", writeln(content))
)
writeln("</",call message name,">")
)
Builder ul(
li("IO"),
li("Lua"),
li("Javascript")
)##并发 Io有非常出色的并发库,其主要组成部分包括协程、actor和future。 ###1.协程 协程是并发的基础。它提供了进程自动挂起和恢复执行的机制。你可以把协程想象成有多个入口和出口的函数。每次yield都会自动挂起当前进程,并把控制转到另一个进程中。 通过在消息前加上@或@@,你可以异步触发消息,前者将返回future,后者会返回nil,并在其自身线程中触发消息。
vizzini := Object clone
vizzini talk := method(
"Fezzik, are there rooks ahead?" println
yield
"No more rhymes now, I mean it." println
yield
)
fezzik := Object clone
fezzik rhyme := method(
yield
"If there are, we'll all be dead." println
yield
"Anybody want a peanut?" println
)
vizzini @@talk
fezzik @@rhyme
Coroutine currentCoroutine pause协程是组成更高级抽象概念(如actor)的基本元素。你可以把actor想象成通用的并发原语,它可以发送消息、处理消息以及创建其它actor。actor接收到的消息是并发的。在Io中,actor把新到达的消息放到队列上,并用协程处理队列中的各个消息。
###2.Actor 和线程相比,actor有巨大的理论优势。一个actor可以改变其自身状态,并且通过严格控制的队列接触其它actor。而多个线程可以不受限制地改变状态。线程容易接受到被称为竞争条件的并发影响。在这种问题中,如果两个线程同时存取资源,可能导致不可预测的后果。
Io的动人之处就在于此,发送异步消息给任何对象就是actor,就这么简单。举一个例子:
slower := Object clone
faster := Object clone
slower start := method(
wait(2)
writeln("slowly")
)
faster start := method(
wait(1)
writeln("quickly")
)
slower start
faster start
"======================" println
slower @@start
faster @@start
Coroutine currentCoroutine pause###3.future 在Io中,future并不是代理实现。future会阻塞到可获得结果为止。future的值一开始是个future对象,但等到结果产生之后,所有future值的实例都会指向结果对象。
futureResult := URL with("http:///") @fetch
writeln("doing other thing ...")
wait(2)
writeln("fetched," futureResult size," bytes")在windows下运行上面代码出现Exception: Object does not respond to 'URL'异常,google了一下,未能解决,望高手指点。
##第三天自习
- 改进本节生成的XML程序,增加空格以显示缩进结构。
Builder := Object clone
Builder indentSize := 4
Builder i := 0
Builder forward := method(
(i * indentSize) repeat(write(" "))
writeln("<",call message name,">")
i = i + 1
call message arguments foreach(arg,
content := self doMessage(arg)
if(content type == "Sequence",
(i * indentSize) repeat(write(" "))
writeln(content)
)
)
i = i -1
(i * indentSize) repeat(write(" "))
writeln("</",call message name,">")
)
Builder ul(
li(p("IO")),
li("Lua"),
li("Javascript")
)- 创建一种使用括号的列表语法
squareBrackets := method(
if(call message arguments asString containsSeq("squareBrackets") not,return call message arguments) //如果不含子数组就直接返回参数列表,参数列表本来就是一个list
list := List clone
call message arguments foreach(arg,
aArg := if(arg asString beginsWithSeq("squareBrackets"),doMessage(arg),arg)
list append(aArg)// 最后一行为返回值
)
list
)
[] println
[1,"kk"] println
[1,["kk",10.5]] println- 改进本节生成xml程序,使其可以处理属性:如果第一个参数时映射(用大括号语法),则为xml添加属性。例如:
book({"author":"Tate"}...)将打印出<book author="Tate">
OperatorTable addAssignOperator(":","addAttribute")
Builder := Object clone
Builder addAttribute := method(
call message arguments println
write(call evalArgAt(0))
write("=\"",call evalArgAt(1),"\"")
)
Builder curlyBrackets := method(
call message arguments foreach(arg,
self doMessage(arg)
)
)
Builder indentSize := 4
Builder i := 0
Builder forward := method(
(i * indentSize) repeat(write(" "))
write("<",call message name)
i = i + 1
args :=call message arguments
arg1 := args at(0)
if( arg1 asString beginsWithSeq("curlyBrackets"),
//arg1 println
self doMessage(arg1)
args remove(arg1)
)
writeln(">")
args foreach(arg,
content := self doMessage(arg)
if(content type == "Sequence",
(i * indentSize) repeat(write(" "))
writeln(content)
)
)
i = i -1
(i * indentSize) repeat(write(" "))
writeln("</",call message name,">")
)
Builder ul(
{"class" : "foo","id" : "language"},
li("Io")
)上面代码会出现异常,不知道如何解决。
<ul
Exception: Sequence does not respond to ':'
---------
Sequence : builder.io 50
Builder curlyBrackets builder.io 50
Builder ul builder.io 49
CLI doFile Z_CLI.io 140
CLI run
















