本文记录了《七周七语言》的读书笔记和在IoLanguageGuide的一些内容。
Io语言同Lua、JavaScript一样是一种原型语言,每个对象都是另一个对象的复制品。
核心优势:拥有大量可定制语法和函数,以及强有力的并发模型。
Io没有关键字,Io语法只不过是把消息全部串联起来,每条消息都会返回一个对象,每条消息也都带有置于括号内的可选参数。在Io中,万事万物都是消息。
被复制的对象就叫做原型。
在
原型语言中,每个对象都不是类的复制品,而是一个实实在在的对象。
Io> "Hi ho, Io" print
发送print消息给字符串"Hi ho, Io",接受者(对象)在左边,消息在右边。
Io不区分类和对象,可以通过复制现有对象创建新对象,现有对象就是原型。
Vehicle := Object clone
对象带有槽(slot),相当于可以为每个对象绑定一个散列表。
使用:= 可以初始化槽并赋值,使用=可以给槽赋值。
Io惯例:类型应以大写字母开头。
对象是槽的容器。发送槽名给对象就可以获得该槽,如果该槽不存在,则调用父对象的槽。
在Ruby和Java中,类是创建对象的模板。在Io中,bruce := Person clone,bruce和Person都是对象。Person还是类型,因为它有type槽。除此之外,它们完全相同。
方法(method())也是对象。可以把它赋给一个槽。
Lobby是主命名空间,包含了所有的已命名对象。
原型编程范型的基本原则:
所有事物都是对象; 所有与对象的交互都是消息; 你要做的不是实例化类,而是复制那些叫做原型的对象; 对象会记住它的原型; 对象有槽; 槽包含对象(包括方法对象); 消息返回槽中的值,或调用槽中的方法; 如果对象无法响应某消息,则它会把该消息发送给自己的原型。
集合:list 和 map
toDos := list("a","b","c)
list(1,2,3,5) average
elvis := Map clone
elvis atPut("home","Graceland")
elvis size
布尔值:和Ruby一样,0是true。只有nil和false是false。且true,false,nil都是单例。
单例:Single := Object clone
Single clone := Single
Io有三种赋值运算符:
operator action
::= Creates slot, creates setter, assigns value
:= Creates slot, assigns value
= Assigns value to slot if it exists, otherwise raises exception
第二天
条件和循环
分号会把两个不同的消息连接起来。while循环带有一个条件和一个用来求值的消息。while(i <= 11,i println ; i = i+1);"This one goes up to 11" printlnfor循环带有一个计数器、一个初始值、一个终止值、一个可选的增量、以及一个带发送者的消息:for(i,1,11,i println);"goes up to 11" printlnfor(i,1,11,2,i println);"goes up to 11" println # 2为增量,每次增加2Io允许额外参数。 for(i,1,11,i println,"extra args");if:if(condition,true code ,false code)if(condition) then(true code) else (false code)
运算符
像面向对象语言一样,很多原型语言也在运算符上用到了语法糖。如"+"和"/"
查看运算符表:
OperatorTable
OperatorTable addOperator("xor",11)
为布尔值增加xor方法:
true xor := method(bool,if(bool,false,true))
false xor := method(bool,if(bool,true,false))
消息:
Io代码中除了注释符和参数之外的逗号外一切都是消息。
一个消息由三部分组成:发送者、目标和参数。在Io中,消息由发送者发送至目标,然后由目标执行该消息。
call方法可以访问任何消息的元消息。
Lobby 打印已经存在的对象
print 打印Lobby的内容
检查对象:
1 获得对象的槽列表:Io> someObject slotNames2 排序显示上表:someObject slotNames sort3 slotSummary以整洁方式展示槽名4 观察方法未编译的版本:获取一个Block的值而不启动它Lobby getSlot("forward")5 在交互模式运行io脚本:doFile("scriptName.io")如果想在某个对象的上下文中执行则:someObject doFile("scriptName.io")doString : 计算一个字符串someObject doString("1+1")6 命令行参数:System args打印附加的命令行参数:System args foreach(k,v,write("'",v,"'\n"))7 launchPathSystem launchPath 槽是源文件最初执行的路径,是当前工作路径。
句法:
Io没有关键字和声明语句。一切都是由消息构成的表达式,每个消息都是一个运行期可访问的对象。
(for,if等看起来的关键字其实都是消息)
BNF描述:(用于描述语言的语法)
exp ::= { message | terminator }message ::= symbol [arguments]arguments ::= "(" [exp [ { "," exp } ]] ")"symbol ::= identifier | number | stringterminator ::= "\n" | ";"
消息参数被当做表达式传递,并由接收者计算。
运算符:一个运算符只是一个名字没有字母的消息或or,and,return中的一个。
赋值:::= 建槽,建setter,赋值
:= 建槽,赋值
= 赋值
source compiles toa ::= 1 newSlot("a", 1)a := 1 setSlot("a", 1)a = 1 updateSlot("a", 1)
数值:
123
123.4560.456.456123e-4123e4123.456e-7123.456e2Hex numbers are also supported (in any casing):0x00x0F0XeE
字符串:
带转义字符的 "str\n" or
不带转义,且可多行输入的
s := """this is a "test".
This is only a test."""
注释:
// , /**/ , #
对象:
在Io中,一切皆对象,所有的动作都是消息。对象由一个包含许多键值(槽)的列表和一个继承自原型的内置列表构成。槽的key是一个符号(一个不可变字符串),value可以是任何类型的对象。
clone和init:
新的对象都是通过复制已有对象创建的。
clone完成后将会调用init槽(如果有的话)。
继承:
对象收到消息-> 消息符合该对象的某个槽,执行
-> 找不到槽,循环向上找该对象的原型
Io命名空间没有全局变量,
根对象被称为Lobby
Dog := Object clone
把Lobby的槽"Dog"设置成一个对象的复制。
方法:
方法就是一中匿名函数,调用的时候创建一个对象储存本地变量,并设置本地原型指针和自己的槽到消息目标上。
用method
一般形式:(最后一个是方法体,其它的是方法参数)
method(<arg name 0>, <arg name 1>, ..., <do message>)
代码块:
Blocks vs. Methods:
共同点:被调用时都创建对象来保存本地变量。
不同点:method中"proto"和"self"被设置到消息对象的目标上。block中"proto"和"self"它们都被设置到创建时的本地对象上。所以一次失败的变量查找,将引起在代码块创建上下文的本地变量中继续查找,而方法则是在消息接收者中继续查找。
eg.
b := block(a,b,a+b)
m := method(a,b,a+b)
b -> block...
m -> exception ...
当一个本地对象被创建,它的self槽被设置(到:method:消息目标,block:创建的上下文中),call槽被设置为一个Call对象,用来访问block信息:
slot returnscall sender locals object of callercall message message used to call this method/blockcall activated the activated method/blockcall slotContext context in which slot was foundcall target current object
以上的槽在某个消息中使用,可以通过反射获得消息的信息
protos : 返回一个对象的继承列表
code : 返回以个方法的字符串表达式
子对象方法体;
resend
super(原型方法名)
code : view source
控制流:
IF:
if(<condition>, <do message>, <else do message>)
if(y < 10, x := y, x := 0)
is the same as:
x := if(y < 10, y, 0)
Like SmallTalk (ifTrue ,ifFalse,ifNil,ifNonNil:
(y < 10) ifTrue(x := y) ifFalse(x := 2)
if(y < 10) then(x := y) elseif(y == 11) then(x := 0) else(x := 2)
LOOP:
3 repeat("foo" print) ==> foofoofoo
while
Arguments:
while(<condition>, <do message>)
for(<counter>, <start>, <end>, <optional step>, <do message>)
break,continue :
loop, repeat, while and for support the break and continue methods. Example:
for(i, 1, 10, if(i == 3, continue) if(i == 7, break) i print )
return :立即返回
import
并发:
协程Coroutines:Io使用用户级协程代替抢占式的系统级线程来实现并发,避免了大量的开销。
Scheduler:用于恢复挂起的协程。先进先出策略。
Actors:拥有自己的异步消息队列的对象。
Io里的每个对象都可以发送异步消息,asyncSend() 返回nil,futureSend()返回future
当一个对象收到一个异步消息时,它将消息放到它的队列里,如果没有队列,就开启一个协程处理队列里的消息。已经排队的消息依序被处理。可以使用yield将控制权转移给其他的协程。
Futures:当结果就绪时,future就会成为结果。允许程序最小化阻塞,同时去除了管理同步的细节。
自动死锁检测 通过检查已连接的future,发现死锁,抛出异常。
future会等待处理完成
命令行会试图打印结果。所以如果结果是一个future,就会等待future结果。
q := method(wait(1))
futureSend(q) # delay 1-second
futureSend(q);nil # no delay nil是结果
Yield:对象在处理它的消息间隙会自动yield。yield会把控制权转移给其他协程。
resume and pause
异常:
raise:通过在一个异常原型上调用raise()触发异常。
try and catch:
try() 方法返回在它内部发生的异常,若没有就返回nil
e := try(<doMessage>)
catch()方法返回:不匹配->异常;匹配->nil
e := try(
//...
)
e catch(Exception,
writeln(e coroutine backtraceString)
)
pass:传递异常到上层处理类,通常在所有catch之后
e := try(// ...)
e catch(Error,// ...) catch(Exception,// ...) pass
自定义异常:复制已有异常:
MyErrorType := Error clone
基本类型:
基本类型是内建在Io中,方法由C实现并且在实例中隐藏了某些数据的对象。所有的基本类型都继承自Object,且都是可变的。
问号?可以判断是否存在某槽:
obj ?foo 相当于 if(obj getSlot("foo"),obj foo)
List:支持数组和枚举操作
append(obj)
size
at(index)
atPut(index,obj)
atInsert(index,obj)
remove(obj)
...
foreach:
foreach,map,select方法有三种用法:
a := list(25,123,12)
一:a foreach(index,value,write(index,":",value,","))
第一个参数是索引,第二个参数是值,第三个参数是每个值要计算的表达式
二:a foreach(v,v println)
去除索引参数
三:a foreach(println)
去掉索引和值参数,仅把表达式作为消息发送给每个迭代的值。
map和select:(我觉得最好翻译成映射和过滤)
select相当于其他语言的filter
map和select的使用方法与foreach无异。
map和select都会返回新的list
序列Sequence:
不可变序列称为Symbol,可变序列称为缓冲Buffer或字符串String。字面量(在源码中被引号引起的)是Symbol。通过调用
asMutable方法可获得Symbol的可变复制体。
size
containsSeq(str) # 是否包含字符串
at(n) # 获得第n个位置的字符(比特)
slice(start,end) # java.lang.String#substring(start,end)
slice(-n) # 获取最后n个字符
slice(start,-end) # 返回去除最后end个字符的字符串
asUppercase
asLowercase
split # 默认按空格分隔字符,返回list
split("") # 按指定字符分隔返回list
asNumber
interpolate # 重新计算#{}中的值,#{}中可以是任意代码,但是必须返回能响应asString方法的对象 name := "Bflee" ; "My name is #{name}" interpolate
Range:一个保存了开始、结束端和如何开始到结束的容器。常用于创建大型有序数据组成的列表,以及作为for方法的替代。
File:
获取代表file的对象:
f := File with("foo.txt")
f remove // 删除
f openForUpdating
f openForAppending
f openForReading
f write("")
f close
Directory:
dir := Directory with("/Users/bflee/")
files := dir files // 列出某目录的所有文件list
items := Directory items // 列出文件夹和文件list
items at(4) name
Directory items map(name) // 列出当前目录所有文件夹和文件名
Direcotry items select(? isDirectory not) map(name) // 列出当前文件夹内所有文件的文件名
Direcotry items select(? isDirecotry not) select(name containsSeq("bak") not) map(name) sort // 列出当前目录所有不包含bak的文件名。
root := Directory clone setPath("C:/") // 获取一个指向特定路径的目录
root fileNames // 获取文件列表
root exists // 测试是否存在
Directory currentWorkingDirectory // 获取当前工作路径 同 System launchPath
Date:
d := Date clone
d now // 设置为当前日期/时间
Date now asNumber // 获取当前日期/时间,以秒表示
Date now year/month/day/hour/minute/second
Date cpuSecondsToRun(100000 repeat(1+1)) // 测试代码执行时间
XML:
# Using the XML parser to find the links in a web page:
SGML // reference this to load the SGML addon
xml := URL with("http://www.yahoo.com/") fetch asXML
links := xml elementsWithName("a") map(attributes at("href"))