`

Io语言导引

阅读更多

Io语言导引

Io第一次接触Io语言的时候我就被其简洁干净的语法打动了(如果你有编程语言的经验,也许15分钟就可以掌握大部分的语法),Io语言的简单、灵活和对并发的良好支持都让人印象深刻。本文翻译自Io语言官网的这篇文章

引言

总览

Io语言是一门基于原型的动态语言,主要思想很大程度上受到了Smalltalk(所有变量都是对象)、Self(基于原型)、NewtonScript(差异化继承)、Act1(并发行为特征)、Lisp(运行时可观测和修改代码)和Lua(小巧可嵌入)的影响。

透视图

过去三十年编程语言研究的重心已经转移到了具备强大的表达力的高级语言(比如Smalltalk),以及性能卓越的底层语言(比如C)。结果就是一系列的中间语言(既不如C快,又不如Smalltalk那样有强大的表达力)诞生出来。Io语言的目的通过高级动态语言的特性——运行时灵活性和极其简单的语法重新定位到语言表达能力上。在Io语言中,所有变量都是对象,所有变量都可以在运行时改变,包括“槽”(后文会提到)、方法和继承关系;所有代码都由表达式组成,可以在运行时任意查看和修改;所有表达式都由动态的信息发送机制组成,包括赋值结构和控制结构。执行上下文本身就是可激活的对象,比如方法、代码块和函数,通过一个可分配的域统一到一起。并发通过actors被设计成更容易实现,并且使用coroutines机制以便于具备伸缩性。

目标

设计成一门:

简单的语言

  • 概念简单一致
  • 容易嵌入和扩展

强大的语言

  • 高度动态的和自省的
  • 高度并发的(通过coroutines和异步i/o)

实用的语言

  • 足够快
  • 跨平台
  • 非限制性的BSD/MIT证书
  • 发行广泛的标准包支持

下载

http://iolanguage.com

安装

首先编译Io vm:

make vm
sudo make install

安装插件依赖

有一些依赖包也许不在你的系统里,所以要自动安装的话,输入:

su -c "sudo make aptget"
或者
su -c "make emerge"
或者
sudo make port

用哪个要看你用哪个包安装器,port那一个是专给OSX准备的。

编译插件

根目录执行:

make

生成的二进制文件会放到_build/binaries子目录中。安装:

sudo make install

或者,如果你想要以链接的方式安装到你的开发文件夹中的话:

sudo make linkInstall

运行单元测试:

make test

有些插件构建失败也没关系,除非你明确需要那个插件。插件只不过是可选组件。

备注

添加特定的插件:

make AddonName

从库上取下代码后,记得执行:

make clean; make

根据源文件生成新的参考文档(在docs/IoReference.html上也可以找到):

make doc

二进制文件

Io会建立两份可执行文件并且把它们替换到二进制文件夹中:

io_static
io

io_static可执行文件包括了原始类型支持的最小集合,都是静态链接到它上面的。io可执行文件则可以加载iovm动态链接库,可以在需要的时候动态加载io插件。

运行Io脚本

例子:

io samples/misc/HelloWorld.io

这种方式下不需要main方法或者对象,脚本在编译后就执行了。

交互模式

运行:

./_build/binaries/io

也可以直接输入:

 

这样就打开了解释器的提示。

你可以直接输入代码解释执行,比如:

Io> "Hello world!" println
==> Hello world!

表达式在Lobby的上下文中执行:

Io> print
[printout of lobby contents]

如果你在home文件夹中有一个.iorc文件,它会在解释器提示前执行。

检视对象

你可以获得一个某对象的槽的列表:

Io> someObject slotNames

如果要排序显示出来:

Io> someObject slotNames sort

要以漂亮的格式来展示,slotSummary方法很便捷:

Io> slotSummary
==>  Object_0x20c4e0:
  Lobby            = Object_0x20c4e0
  Protos           = Object_0x20bff0
  exit             = method(...)
  forward          = method(...)

更进一步查看:

Io> Protos
==>  Object_0x20bff0:
  Addons           = Object_0x20c6f0
  Core             = Object_0x20c4b0
 
Io> Protos Addons
==>  Object_0x20c6f0:
  ReadLine         = Object_0x366a10

看起来只有ReadLine在插件里面,没有别的了。同时,也打印出了反编译出的版本号。

Io> Lobby getSlot("forward")
==> # io/Z_Importer.io:65
method(
    Importer import(call)
)

doFile和doString

脚本可以以交互模式运行,使用doFile方法如下:

doFile("scriptName.io")

doFile的执行上下文正是它的接收器(receiver),在这里就是lobby。上下文的其它对象来执行脚本的话,可以把doFile消息发送给它。

someObject doFile("scriptName.io")

doString方法可以用来执行生成一个string:

Io> doString("1+1")
==> 2

同样,也可以指定一个上下文:

someObject doString("1 + 1")

命令行参数

打印出命令行参数的示例:

System args foreach(k, v, write("'", v, "'\n"))

launchPath

System的“launchPath”槽用来设定初始源文件执行的位置。当交互模式提示符出现(不显式指定源文件路径),launchPath就是当前的工作目录:

System launchPath

句法

表达式

Io语言没有关键字或者声明语句。一切都是由表达式组成的消息,每一个都是在运行时可以被访问的对象。正式的BNF表述如下:

exp        ::= { message | terminator }
message    ::= symbol [arguments]
arguments  ::= "(" [exp [ { "," exp } ]] ")"
symbol     ::= identifier | number | string
terminator ::= "\n" | ";"

考虑到性能的原因,String和Number字面消息会缓存结果在消息对象中。

消息

消息参数以表达式的方式传入,并在receiver中执行。

根据参数取值选择逻辑可以用来实现控制流,比如:

for(i, 1, 10, i println)
a := if(b == 0, c + 1, d)

上面的代码中,for和if都是普通的消息而已,并不是什么关键字。

类似的,动态赋值可以用来实现枚举的功能,而不需要一个封闭的代码块:

people select(person, person age < 30)
names := people map(person, person name)

像map、select之类的方法往往被用于表达式直接过滤值集合:

people select(age < 30)
names := people map(name)

有一些运算符(包括赋值)的语法糖,在编译成消息树以后被Io的宏执行:

Account := Object clone
Account balance := 0
Account deposit := method(amount,
    balance = balance + amount
)
 
account := Account clone
account deposit(10.00)
account balance println

像Self语言一样,Io的语法并不区分访问方法来获取值还是直接获取值。

操作符

操作符只是一个普通的消息,而且这个消息不包含任何字母(or、and和return除外):

1 + 2

它会被编译成:

1 +(2)

如果你需要括号分组:

1 +(2 * 4)

符合C的优先级顺序:

1 + 2 * 3 + 4

会被转换为:

1 +(2 *(3)) +(4)

用户定义的操作符(非标准的操作符名字)是自左向右结合运算的。

赋值

Io有三种赋值运算符:

operator     action
::=          Creates slot, creates setter, assigns value
:=           Creates slot, assigns value
=            Assigns value to slot if it exists, otherwise raises exception

赋值运算符也是普通消息,它们的方法都可以被覆写:

source     compiles to
a ::= 1    newSlot("a", 1)
a := 1     setSlot("a", 1)
a = 1      updateSlot("a", 1)

本地对象中,updateSlot被覆写,如果这个槽不存在,这个槽就会被更新到对象中。这个操作不需要explicit显示声明就可以做到。

数字

有效的数字格式:

123
123.456
0.456
.456
123e-4
123e4
123.456e-7
123.456e2

十六进制数亦被支持(不限大小写):

0x0
0x0F
0XeE

字符串

字符串用一组双引号+转义符来定义:

s := "this is a \"test\".\nThis is only a test."

或者,不使用转义字符的话可以写成多行(用三个双引号):

s := """this is a "test".
This is only a test."""

注释

注释支持//, /**/和#风格:

a := b // add a comment to a line
 
/* comment out a group
a := 1
b := 2
*/

“#”风格在Unix中很有用:

#!/usr/local/bin/io

就这些!一切都是表达式。这些就是控制流、对象、方法、异常和其他语法表达式,以及语义。

对象

概览

通过对概念的统一,Io的导引描述了简单和强大的原则。

concept               unifies
scopable blocks       functions, methods, closures
prototypes            objects, classes, namespaces, locals
messages              operators, calls, assigns, var access

原型

一切都是对象,包括存储代码块的本地变量和命名空间,而所有对象又都是消息(包括赋值操作符)。对象可以由一串键值对组成,他们被称为“槽”,每一个对象都自内部对象继承而来,被继承的对象被称为“原型”。槽的键可以是一个符号(唯一的无意义序列),值可以是任意对象类型。

clone和init

新建的对象通过clone已有对象来实现。clone出来的东西其实是一个空对象,但是在它的原型列表中,可以找到它的父对象。在这个过程后,新建对象的init槽会被调用,以便初始化对象自己。就像NewtonScript一样,槽在Io中是符合“创建-写入”模式的:

me := Person clone

添加一个实例变量或者方法:

myDog name := "rover"
myDog sit := method("I'm sitting\n" print)

如果存在init方法的话,clone后init会被自动调用。

继承

对象收到消息的时候,如果消息符合该对象的某一个槽,就执行,如果找不到,那么就一层一层向上找它的原型。查找循环会被检测到,它是不被允许的。如果匹配的槽包含一个可激活的对象,比如Block或者CFunction,它包含任何其他值类型,就会被递归调用。Io命名空间中没有全局变量,根对象被称为Lobby。

因为没有类,只有原型,那么子类和实例本身也就没有任何区别了。这里有一个创建相同子类的例子:

Io> Dog := Object clone
==> Object_0x4a7c0

以上代码设置了Lobby的槽“Dog”为Object对象,这个新对象的原型列表只包含一个对Object的引用,本质上指明了这就是Object的子类。实例变量和方法继承自这个原型列表。如果设置了这个槽,创建新槽并不会改变它的原型对象:

Io> Dog color := "red"
Io> Dog
==> Object_0x4a7c0:
  color := "red"

多重继承

你可以添加任意数量的原型到对象的原型列表上。当对象收到一个消息的时候,它会深度搜索原型链寻找匹配。

方法

方法就是一种匿名函数,调用的时候,会创建一个对象存储在本地变量集合里面,并且设置这个对象的原型指针和它自己的槽到相应的消息上面。这个Object的method()方法可以用来创建对象:

method((2 + 2) print)

对象中使用这个method的例子:

Dog := Object clone
Dog bark := method("woof!" print)

以上代码创建了一个Object的子类“Dog”,并且添加了“bark”槽,这个槽包含了打印“woof!”的代码块。调用示例:

Dog bark

默认返回值就是最后一行表达式语句的结果。

参数

方法定义的时候可以带参数:

add := method(a, b, a + b)

总的来说,形式如下:

method(<arg name 0>, <arg name 1>, ..., <do message>)

代码块

除去词法的变量范围以外,代码块和方法一样。变量查找会在代码块创建的上下文中继续,而不是在调用代码块的消息的上下文中继续。一个代码块可以使用Object的block()方法来创建:

b := block(a, a + b)

代码块和方法

有时候这两个概念会引起混乱,所以有必要再细细解释一下。代码块和方法都可以创建在被调用时可以持有本地变量的对象。不同之处就在于本地对象的“proto”和“self”槽设置到哪里去。方法中,这些槽设置到消息对象的目标去,而在代码块中,它们是被设置到代码块创建时的本地对象中去的。所以一次失败的变量查找,将引起在代码块创建上下文的本地变量中继续查找,而方法则是在消息接收者中继续查找。

call和self槽

当本地对象被创建,self槽被设置

当本地对象创建,它的self槽和call槽会被设置到Call对象中,以用以获取关于代码块调用的信息:

slot               returns
call sender        locals object of caller
call message       message used to call this method/block
call activated     the activated method/block
call slotContext   context in which slot was found
call target        current object

可变参数

本地变量的“call message”槽可以用来获取不定参数消息。见if()的实现:

myif := method(
    (call sender doMessage(call message argAt(0))) ifTrue(
    call sender doMessage(call message argAt(1))) ifFalse(
    call sender doMessage(call message argAt(2)))
)
 
myif(foo == bar, write("true\n"), write("false\n"))

doMessage()方法把参数传入receiver的上下文中,一个简洁的写法是使用evalArgAt():

myif := method(
    call evalArgAt(0) ifTrue(
    call evalArgAt(1)) ifFalse(
    call evalArgAt(2))
)
 
myif(foo == bar, write("true\n"), write("false\n"))

Forward

如果对象不响应消息,而且forward方法存在的话,会调用forward方法。这个例子是显示怎样打印查找失败消息的:

MyObject forward := method(
    write("sender = ", call sender, "\n")
    write("message name = ", call message name, "\n")
    args := call message argsEvaluatedIn(call sender)
    args foreach(i, v, write("arg", i, " = ", v, "\n") )
)

重发

以self作为上下文,将当前消息发给receiver的原型链:

A := Object clone
A m := method(write("in A\n"))
B := A clone
B m := method(write("in B\n"); resend)
B m

打印:

in B
in A

重发其他消息给receiver的原型时,会使用到super。

Super

如果你需要直接发消息给原型:

Dog := Object clone
Dog bark := method(writeln("woof!"))
 
fido := Dog clone
fido bark := method(
    writeln("ruf!")
    super(bark)
)

重发和super都在Io中实现了。

自省

使用以下方法你可以自省Io的命名空间。也有方法用来在运行时修改这些属性值。

slotNames

slotNames方法返回一个对象槽名字的列表:

Io> Dog slotNames
==> list("bark")

protos

protos方法返回对象继承的一个列表:

Io> Dog protos
==> list("Object")

getSlot

getSlot方法用于获取一个槽的实际值:

myMethod := Dog getSlot("bark")

前文中,我们设置了本地对象的myMethod槽为bark方法。你如果需要myMethod方法,但是又不调用它,你可以通过getSlot来获取:

otherObject newMethod := getSlot("myMethod")

code

方法的参数和表达式是可以自省的。 一个好用的办法的就是code方法,返回一个源代码的字符串:

Io> method(a, a * 2) code
==> "method(a, a *(2))"

控制流

true、false和nil

true、false和nil都是单例,nil一般用于标识一个未设置值或者丢失了的值。

比较方法

比较方法:

==, !=, >=, <=, >, <

返回true或者false,compare()方法用于实现真正的比较,返回-1、0或者1,分别表示小于、相等和大于。

if、then、else

if()方法的形式:

if(<condition>, <do message>, <else do message>)

例子:

if(a == 10, "a is 10" print)

else参数是可选的。如果条件表达式执行为false或者nil,都被视作false。

执行结果也可以返回:

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

和如下形式一样:

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

条件可以这样用:

if(y < 10) then(x := y) else(x := 2)

支持elseif():

if(y < 10) then(x := y) elseif(y == 11) then(x := 0) else(x := 2)

ifTrue、ifFalse

支持Smalltalk风格的ifTrue、ifFalse、ifNil和ifNonNil方法:

(y < 10) ifTrue(x := y) ifFalse(x := 2)

注意条件表达式必须用圆括号括起来。

循环

循环方法支持无限循环:

loop("foo" println)

repeat

Number的repeat方法可以用于把一个对象的方法重复执行你给定的次数:

3 repeat("foo" print)
==> foofoofoo

while

形式:

while(<condition>, <do message>)
 
a := 1
while(a < 10,
    a print
    a = a + 1
)

for

形式:

for(<counter>, <start>, <end>, <optional step>, <do message>)

start和end消息只在循环开始时执行一次:

for(a, 0, 10,
    a println
)

使用step:

for(x, 0, 10, 3, x println)

打印:

0
3
6
9

要反转这个循环的话,使用负值作为step:

for(a, 10, 0, -1, a println)

注:first值是循环的起始值,last值是标志环完成时的值,所以1到10的循环会执行10次而0到10的循环会执行11次。

break、continue

loop、repeat、while和for都是支持break和continue的:

for(i, 1, 10,
    if(i == 3, continue)
    if(i == 7, break)
    i print
)

输出:

12456

return

代码块中执行到任何语句时都可以返回:

Io> test := method(123 print; return "abc"; 456 print)
Io> test
123
==> abc

break、continue和return都通过存取stopStatus这个监控循环和消息的内部值来工作。

引入

Importer原型实现了Io内置的引入机制。你只需要把你的原型放到它们各自的文件中,文件名以io结尾,这个Importer在原型第一次被用到的时候会自动引入它们。默认的搜索路径是当前工作目录,你也可以调用addSearchPath()来添加路径。

并发

Coroutines

Io使用coroutines(协程,这里指用户层面的线程协作)而不是操作系统抢占式的线程实现方式。这就减少了潜在的因为本地线程和数千个活跃线程切换引起的消耗(内存、系统调用、锁和缓存问题等等)。

Scheduler

Scheduler对象用来重新开始已经挂起的coroutines(协程,见上文)。当前的scheduling系统使用无优先级的FIFO策略。

Actors

actor是一个拥有自己线程的对象(拥有自己的协程),用于处理异步消息队列。任何对象可以以异步消息的方式发送出去,你只需要重写asyncSend()或者futureSend() 消息:

result := self foo // synchronous
futureResult := self futureSend(foo) // async, immediately returns a Future
self asyncSend(foo) // async, immediately returns nil

当对象接收到异步消息的时候,会把消息放到队列里面,如果队列里没有的话,会启动一个协程来处理队列中的消息。队列消息是顺序处理的(FIFO)。Control可以通过调用yield用来挂起当前处理流程,让出资源:

obj1 := Object clone
obj1 test := method(for(n, 1, 3, n print; yield))
obj2 := obj1 clone
obj1 asyncSend(test); obj2 asyncSend(test)
while(Scheduler yieldingCoros size > 1, yield)

会打印出112233,以下是一个更真实的例子:

HttpServer handleRequest := method(aSocket,
    HttpRequestHandler clone asyncSend(handleRequest(aSocket))
)

Futures

Io的future是透明的,当结果准备好的时候,它们就是结果,如果一个消息被发送给future,就会被挂起等待,直到结果准备好。透明的future很强大,因为它们允许程序员尽可能地减小阻塞,把程序员从繁重的异步管理的细节中解脱出来。

自动死锁检测

使用future的一个优点是当等待的时候,它会检测等待是否在等待中会产生死锁,如果会的话就抛出异常。

Futures和命令行接口

命令行会打印表达式的结果,如果结果是Future的话,直到结果返回再打印:

Io> q := method(wait(1))
Io> futureSend(q)
[1-second delay]
==> nil

如果不想这样,不返回Future就行:

Io> futureSend(q); nil
[no delay]
==> nil

Yield

对象的异步消息在执行的时候会自动转让控制权,yield方法只是在要明确让出CPU资源的时候调用。

Pause和Resume

暂停和恢复对象,请参见并发方法部分。

异常

Raise

在异常的原型中调用raise(),表示异常被抛出了。

Exception raise("generic foo exception")

Try和Catch

要捕获异常,使用Object原型的try()方法。try()会捕获任何异常并返回,如果没有异常就返回nil。

e := try(<doMessage>)
To catch a particular exception, the Exception catch() method can be used. Example:
e := try(
    // ...
)
 
e catch(Exception,
    writeln(e coroutine backtraceString)
)

第一个参数是要捕获的异常类型,catch()返回异常。

Pass

要重新抛出异常的话,使用pass方法。它可以把异常抛给下一个外部的异常处理器,我们往往在所有的catch都无法捕获到所需异常的时候抛出:

e := try(
    // ...
)
 
e catch(Error,
    // ...
) catch(Exception,
    // ...
) pass

自定义异常

自定义异常类型可以clone Exception来实现:

MyErrorType := Error clone

Primitives

Primitives(原语类型)是一组Io内建的对象,它们的方法通常使用C实现并且存放了一些隐藏数据在内。举例来说,Number包含了一个double精度的浮点数,并且可以像C函数一样计算。所有的Io原语类型继承自Object原型,并且是不可变对象。也就是说这些方法都是不可变的。

这这篇文档不是想要成为参考手册,只不过是想提供一个基本原语类型的概览,给用户一个入门,提供一个基础认识,要了解详情请参见参考手册。

Object

问号操作符

有时候想要在某方法存在的情况下调用(避免抛出找不到方法的异常):

if(obj getSlot("foo"), obj foo)

可以用问号操作符写成这样:

obj ?foo

链表

链表是一个存放引用的数组,支持标准的数组操作和枚举方法。

创建空链表:

a := List clone

用list()方法创建任意链表:

a := list(33, "a")

添加元素:

a append("b")
==> list(33, "a", "b")

获取大小:

a size
==> 3

根据下标获取元素(从0开始):

a at(1)
==> "a"

设置元素:

a atPut(2, "foo")
==> list(33, "a", "foo", "b")
 
a atPut(6, "Fred")
==> Exception: index out of bounds

删除元素:

a remove("foo")
==> list(33, "a", "b")

插入:

a atInsert(2, "foo")
==> list(33, "a", "foo", "56")

foreach

foreach、map和select方法可以以三种形式调用:

 

Io> a := list(65, 21, 122)

Io> a foreach(i, v, write(i, ":", v, ", "))
==> 0:65, 1:21, 2:122,

第二种形式是略去下标的:

Io> a foreach(v, v println)
==> 65
21
122

第三种形式:

Io> a foreach(println)
==> 65
21
122

map和select

map和select(有的语言里面叫filter)方法允许执行任意表达式:

Io> numbers := list(1, 2, 3, 4, 5, 6)
 
Io> numbers select(isOdd)
==> list(1, 3, 5)
 
Io> numbers select(x, x isOdd)
==> list(1, 3, 5)
 
Io> numbers select(i, x, x isOdd)
==> list(1, 3, 5)
 
Io> numbers map(x, x*2)
==> list(2, 4, 6, 8, 10, 12)
 
Io> numbers map(i, x, x+i)
==> list(1, 3, 5, 7, 9, 11)
 
Io> numbers map(*3)
==> list(3, 6, 9, 12, 15, 18)

map和select方法返回新的链表,如果直接在原有链表上操作,可以使用selectInPlace()和mapInPlace()。

Sequence

不变的Sequence被称为Symbol,而可变的Sequence和Buffer或者String是等价的。Literal strings(被双引号引起来的这种string)就是Symbols,它们是不能改变的。但是调用asMutable可以生成一个可变的字符串:

"abc" size
==> 3

检查是否存在子串:

 

"apples" containsSeq("ppl")

==> true

获取第N个char(byte):

"Kavi" at(1)
==> 97

切割:

"Kirikuro" slice(0, 2)
==> "Ki"
 
"Kirikuro" slice(-2)  # NOT: slice(-2, 0)!
==> "ro"
 
Io> "Kirikuro" slice(0, -2)
# "Kiriku"

去空格:

"  abc  " asMutable strip
==> "abc"
 
"  abc  " asMutable lstrip
==> "abc  "
 
"  abc  " asMutable rstrip
==> "  abc"

大小写转换:

"Kavi" asUppercase
==> "KAVI"
"Kavi" asLowercase
==> "kavi"

切割:

"the quick brown fox" split
==> list("the", "quick", "brown", "fox")

根据其它字符切割:

"a few good men" split("e")
==> list("a f", "w good m", "n")

转换成Number:

"13" asNumber
==> 13
 
"a13" asNumber
==> nil

字符串插入:

name := "Fred"
==> Fred
"My name is #{name}" interpolate
==> My name is Fred

字符串插入会代换#{}里面的东西,代码可以包含循环等等,但是最后必须返回一个String。

Ranges

一个包含起始和结束,还有怎样从开始执行到结束的指令。当创建一个很大的序列数据链表的时候会很有用,它可以很容易被转成list,或者使用for()被替换。

Range规范

每个对象都可以用在Ranges里面,只要实现nextInSequence方法。这个方法接受一个可选参数,表示跳过(skip)多少个对象,然后返回下一个对象,默认的这个skip值是1:

Number nextInSequence := method(skipVal,
    if(skipVal isNil, skipVal = 1)
    self + skipVal
)

有了Number的这个方法,你就可以使用Number的Range了:

1 to(5) foreach(v, v println)

上面代码会打印1到5,一个一行。

File

几个方法:openForAppending、openForReading、openForUpdating,删除的话就是remove方法:

f := File with("foo.txt)
f remove
f openForUpdating
f write("hello world!")
f close

Directory

创建一个文件夹对象:

dir := Directory with("/Users/steve/")

获取一个文件对象的列表:

files := dir files
==> list(File_0x820c40, File_0x820c40, ...)

获取文件+文件夹列表:

 

items := Directory items ==> list(Directory_0x8446b0, File_0x820c40, …)

items at(4) name
==> DarkSide-0.0.1 # a directory name

建立一个文件夹对象:

 

root := Directory clone setPath("c:/") ==> Directory_0x8637b8

root fileNames
==> list("AUTOEXEC.BAT", "boot.ini", "CONFIG.SYS", …)

测试文件是否存在:

Directory clone setPath("q:/") exists
==> false

获取当前工作目录:

Directory currentWorkingDirectory
==> "/cygdrive/c/lang/IoFull-Cygwin-2006-04-20"

Date

创建一个日期实例:

d := Date clone

设置到当前时间:

d now

以number方式获取时间:

Date now asNumber
==> 1147198509.417114

获取时间的每一部分:

d := Date now
==> 2006-05-09 21:53:03 EST
 
d
==> 2006-05-09 21:53:03 EST
 
d year
==> 2006
 
d month
==> 5
 
d day
==> 9
 
d hour
==> 21
 
d minute
==> 53
 
d second
==> 3.747125

看执行代码的时间是多少:

Date cpuSecondsToRun(100000 repeat(1+1))
==> 0.02

网络

Io支持异步连接,但是像读写socket的行为是同步的,因为调用coroutine是无法预期的,直到socket完成这步操作,或者超时出现。

创建一个URL对象:

url := URL with(http://example.com/)

获取一个URL:

data := url fetch

文件流:

url streamTo(File with("out.txt"))

一个简单的whois客户端:

whois := method(host,
    socket := Socket clone \
        setHostName("rs.internic.net") setPort(43)
    socket connect streamWrite(host, "\n")
    while(socket streamReadNextChunk, nil)
    return socket readBuffer
)

最简单的服务端:

 

WebRequest := Object clone do( handleSocket := method(aSocket, aSocket streamReadNextChunk request := aSocket readBuffer \ betweenSeq("GET ", " HTTP") f := File with(request) if(f exists, f streamTo(aSocket) , aSocket streamWrite("not found") ) aSocket close ) ) WebServer := Server clone do( setPort(8000) handleSocket := method(aSocket, WebRequest clone asyncSend(handleSocket(aSocket)) ) )

WebServer start

XML

使用XML转换器来找到网页的链接:

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"))

Vector

Vector用在Sequence原语类型上面,定义为:

Vector := Sequence clone setItemType("float32")

Sequence原语类型支持浮点32位操作的SIMD增强。当前包含add、subtract、multiple和divide,但是未来会支持更多数学、逻辑和字符串操作。小例子:

iters := 1000
size := 1024
ops := iters * size
 
v1 := Vector clone setSize(size) rangeFill
v2 := Vector clone setSize(size) rangeFill
 
dt := Date secondsToRun(
    iters repeat(v1 *= v2)
)
 
writeln((ops/(dt*1000000000)) asString(1, 3), " GFLOPS")

在2Ghz的Mac笔记本上跑会输出:

1.255 GFLOPS

相似的C代码(SIMD强化)会输出:

0.479 GFLOPS

从这个例子看,Io是要比单纯的C快大概三倍。

Unicode

Sequences

符号、字符串和vectors都统一到一个Sequence原型中,Sequence是一个包含所有可用的硬件数据类型的数组:

uint8, uint16, uint32, uint64
int8, int16, int32, int64
float32, float64

编码

Sequence有编码属性:

number, ascii, ucs2, ucs4, utf8

UCS-2和UCS-4分别是UTF-16、UTF-32的等宽字符版本。String只不过是一个带有文本编码的Sequence而已;Symbol(符号)是不可变的String;而Vector则是具备数字编码的Sequence罢了。

UTF编码是高位优先存储的。

除了输入输出,所有的字符串都是等宽编码。这种设计带来了简化实现,代码层面也可以分享vector和string的操作,根据下标快速访问,以及Sequence操作的SIMD支持。所有Sequence方法会自动做必要的类型转换。

源代码

Io的源文件使用UTF8编码,当源文件被读入,符号和字符串被存储时按照最小等宽编码。例子:

Io> "hello" encoding
==> ascii
 
Io> "π" encoding
==> ucs2
 
Io> "∞" encoding
==> ucs2

来查看内部实现:

Io> "π" itemType
==> uint16
 
Io> "π" itemSize
==> 2

转换

Sequence对象有一组转换方法:

asUTF8
asUCS2
asUCS4

嵌入

规约

Io的实现代码C代码是按照面向对象风格来完成的,结构体成了对象,而函数变成了方法。熟悉这些会帮助你理解那些嵌入式的API。

结构体

成员都是小写字母开头,骆驼命名法:

typdef struct
{
    char *firstName;
    char *lastName;
    char *address;
} Person;

函数

函数命名以结构体开头,然后用一下划线连接,再加上真正的方法名。每一个结构体都有一个new函数和一个free函数:

List *List_new(void);
void List_free(List *self);

所有除了new以外的方法都有一个作为首参的结构——self。方法名是符合关键字的格式,也就是说,用下划线串起一组词来描述这个方法:

int List_count(List *self); // no argument
void List_add_(List *self, void *item); // one argument
void Dictionary_key_value_(Dictionary *self,
    char *key, char *value);

文件名

每一个结构体都有它自己的.h和.c文件。除去后缀,文件名和结构体名字一致。文件包含该结构体所有的方法。

IoState

IoState可以被认为是Io的“虚拟机”的实例。尽管这里“虚拟机”并不准确,因为它意味着某种特定的实现类型。

多状态

Io是多状态的,这意味着它被设计来支持多种状态实例,而这些实例可以存在在同一个进程中。这些实例互相独立,不共享内存,所以它们可以被不同操作系统线程访问,尽管同一时间只能访问一个。

创建一个状态

这里有一个创建和变更状态的例子:

#include "IoState.h"
 
int main(int argc, const char *argv[])
{
    IoState *self = IoState_new();
    IoState_init(self);
    IoState_doCString_(self, "writeln(\"hello world!\"");
    IoState_free(self);
    return 0;
}

Values

我们可以获取返回值并且查看和打印:

IoObject *v = IoState_doCString_(self, someString);
char *name = IoObject_name(v);
printf("return type is a ‘%s', name);
IoObject_print(v);

检查值的类型

有一些简便的宏来做快速的类型检查:

if (ISNUMBER(v))
{
    printf("result is the number %f", IoNumber_asFloat(v));
}
else if(ISSEQ(v))
{
    printf("result is the string %s", IoSeq_asCString(v));
}
else if(ISLIST(v))
{
    printf("result is a list with %i elements",
        IoList_rawSize(v));
}

注:返回值总是Io对象,你可以在Io/libs/iovm/source的头文件中找到C级别的方法,比如这样的函数:IoList_rawSize()。

绑定

未来会在文档中补充绑定和插件的部分(原文如此)。

文章系本人原创,转载请注明作者和出处

注:本博客已经迁移到个人站点 http://www.raychase.net/ ,欢迎大家访问收藏,本ITEye博客在数日后将不再更新。
0
4
分享到:
评论

相关推荐

    AVR 系列MCU 导引.pdf

    ### AVR系列MCU导引知识点解析 #### 一、AVR系列MCU的特点 - **在系统编程(ISP)与在应用编程(IAP)**:AVR单片机支持程序的在系统编程(In-System Programming, ISP),使得用户可以在不取出芯片的情况下通过编程器...

    ACCP5.0S1结业测试

    复习资料的开篇标识“079结业考试笔试复习题STB”虽然未提供具体内容,但很可能作为复习指导的导引,帮助学员快速定位到相关复习章节,针对性地进行备考。结业测试通常涵盖多样的题型,例如选择题能够检测理论知识...

    简单的C#AGV地图编辑器

    【C# AGV地图编辑器】是一款基于C#编程语言开发的简单易用的地图编辑工具,主要用于设计和创建AGV(自动导引车)运行的环境地图。在自动化仓储和物流系统中,AGV需要在预设的地图上进行导航,因此,这样的编辑器对于...

    信捷PLC应用实例解析:随机密码、动态验证码与分期付款锁机系统的实现

    内容概要:本文详细介绍了信捷PLC在多个应用场景中的具体实现,包括随机密码生成、动态验证码、动态分期付款功能及锁机例程。首先探讨了随机密码生成,通过PLC的随机数生成功能并结合数学运算,实现了4位随机密码。其次,讲解了动态验证码的实现,利用PLC的实时时钟和通信功能,使验证码随时间动态变化。再次,介绍了动态分期付款功能,通过监测支付信号和比较已支付金额与总金额,实现分期付款的控制。最后,讨论了锁机例程,通过状态继电器和时间窗控制,确保设备在特定条件下不被随意使用。每个部分都提供了详细的梯形图代码和注释,帮助读者理解和实现。 适合人群:对PLC编程有一定基础的技术人员,尤其是从事工业自动化领域的工程师。 使用场景及目标:适用于需要增强设备安全性、提高验证机制可靠性的工业控制系统。通过学习这些例程,工程师可以在实际项目中灵活运用PLC实现复杂的功能,如设备访问控制、支付管理等。 其他说明:文中不仅提供了具体的代码实现,还分享了一些实用技巧和注意事项,如密码比对策略、时间同步校验、多品牌PLC移植建议等。此外,还提到了一些防破解措施,增强了系统的安全性。

    213000-fbo-ggs-Linux-x64-Oracle-shiphome.zipogg21.3安装包,适用于经典架构

    213000-fbo-ggs-Linux-x64-Oracle-shiphome.zip ogg21.3安装包,适用于经典架构

    基于Stanley算法与预瞄距离自适应的CarSim与Simulink联合仿真模型及其应用

    内容概要:本文介绍了基于Stanley算法和预瞄距离自适应机制的CarSim与Simulink联合仿真模型。Stanley算法用于路径跟踪,通过计算横向和航向偏差调整车辆转向角;预瞄距离自适应机制根据车辆速度动态调整预瞄距离,确保在不同速度和路况下都能灵活应对。CarSim提供高精度车辆动力学模型,Simulink则负责算法实现和系统集成。文中还分享了多个实用技巧,如速度单位转换、PID控制器参数调整、数据同步问题解决等,并提供了完整的模型文件供下载。 适合人群:从事自动驾驶研究的技术人员、高校师生及相关领域的研究人员。 使用场景及目标:适用于自动驾驶路径跟踪的研究与开发,旨在提高车辆在不同速度和路况下的路径跟踪性能,减少横向误差,增强行驶稳定性。 其他说明:文中提到的模型文件包括Carsim参数配置文件cpar、Simulink模型文件及详细参考资料,有助于快速搭建并调试联合仿真环境。

    西门子S7-1200 PLC在污水处理项目中的Modbus通讯与PID控制应用详解

    内容概要:本文详细介绍了西门子S7-1200 PLC在污水处理项目中的应用,涵盖多个关键技术模块。首先讨论了模拟量转换,通过具体的代码示例展示了如何将模拟量信号转换为可用于控制的数值。接下来探讨了电动阀控制,解释了如何利用逻辑指令实现电动阀的开关控制。液位控制部分则通过比较指令实现了液位的精准调控。Modbus通讯部分讲解了如何通过Modbus协议控制变频器,包括通讯参数的配置和数据传输的具体实现。PID控制部分详细解析了PID控制器的参数设置及其在污水处理中的应用。最后,PUT与 GET指令的应用确保了主站与从站之间的数据同步。此外,文中还分享了一些实战经验和调试技巧,如模拟量处理的基本方法、Modbus通讯的注意事项以及PID控制的实际应用。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是对PLC编程和污水处理控制系统感兴趣的读者。 使用场景及目标:①帮助工程师理解和掌握西门子S7-1200 PLC在污水处理项目中的具体应用;②提供详细的代码示例和实战经验,便于读者快速上手并应用于实际项目;③解决常见问题,提高系统的稳定性和可靠性。 其他说明:文中不仅涵盖了理论知识,还包括大量的实战经验和调试技巧,有助于读者更好地应对实际项目中的挑战。

    【A股温度计】www.agwdj.com 镜像版程序V1.0

    【A股温度计】www.agwdj.com 镜像版程序V1.0说明 •通过数据可视化技术,将复杂的A股市场数据转化为直观的图形界面,帮助投资者快速把握市场脉搏。 【核心功能】 •全景视角:突破信息碎片化局限,快速定位涨跌分布,一眼锁定今日热点板块 •板块排序:基于申万行业分类标准,对31个一级行业和131个二级行业实时动态排序 •硬件适配:智能适配不同分辨率屏幕,4K以上屏幕显示信息更多(视觉更佳) •智能缩放:A股全图让大A市场5000+个股同屏显示(支持鼠标滚轮及触摸设备5级缩放) 【三秒原则】 •三秒看懂:通过精心设计的视觉图形,让用户在三秒内看清市场整体状况 •三秒定位:智能算法让大成交额个股和热点板块自动靠前,快速定位机会 •三秒操作:极简的界面,让用户减少操作 【使用场景】 •盘前准备:快速了解隔夜市场变化,制定当日策略 •盘中监控:实时跟踪市场动向,及时把握当日机会 •盘后复盘:全面分析当日市场表现,总结经验教训 【适合人群】 •个人用户:快速了解市场整体趋势变化,辅助决策 •专业人员:获取每天市场的数据云图支持研究工作 •金融机构:作为投研系统的可视化补充组件 •财经媒体:制作专业市场分析图表和报道 【市场切换】 •默认加载"A股全图",可切换单独显示的类型如下: •上证A股/深证A股/北证A股/创业板/科创板/ST板块/可转债/ETF 【程序优势】 •运行环境:纯PHP运行(无需安装任何数据库) •数据更新:实时同步→A股温度计→www.agwdj.com •显示优化:自动适配8K/4K/2K/1080P等不同分辨率的屏幕 •设备兼容:对市面上主流的设备及浏览器做了适配(检测到手机/平板/电视等默认Chrome/Firefox/Edge内核过低的情况会自动提示) 【其他说明】 •A股温度计程序演示网址:https://www.agwdj.com

    汽车车载网络系统检修.ppt

    汽车车载网络系统检修.ppt

    【KUKA 机器人资料】:KUKA 机器人初级培训教材.pdf

    KUKA机器人相关文档

    基于Matlab的模拟退火算法在旅行商问题(TSP)优化中的应用及其实现

    内容概要:本文详细介绍了利用Matlab实现模拟退火算法来优化旅行商问题(TSP)。首先阐述了TSP的基本概念及其在路径规划、物流配送等领域的重要性和挑战。接着深入讲解了模拟退火算法的工作原理,包括高温状态下随机探索、逐步降温过程中选择较优解或以一定概率接受较差解的过程。随后展示了具体的Matlab代码实现步骤,涵盖城市坐标的定义、路径长度的计算方法、模拟退火主循环的设计等方面。并通过多个实例演示了不同参数配置下的优化效果,强调了参数调优的重要性。最后讨论了该算法的实际应用场景,如物流配送路线优化,并提供了实用技巧和注意事项。 适合人群:对路径规划、物流配送优化感兴趣的科研人员、工程师及高校学生。 使用场景及目标:适用于需要解决复杂路径规划问题的场合,特别是涉及多个节点间最优路径选择的情况。通过本算法可以有效地减少路径长度,提高配送效率,降低成本。 其他说明:文中不仅给出了完整的Matlab代码,还包括了一些优化建议和技术细节,帮助读者更好地理解和应用这一算法。此外,还提到了一些常见的陷阱和解决方案,有助于初学者避开常见错误。

    BYVIN电动四轮车控制器代码详解:STM32F4硬件与软件设计

    内容概要:本文详细介绍了BYVIN(比德文)电动四轮车控制器的技术细节,涵盖了硬件设计和软件实现两大部分。硬件方面,提供了PCB文件和PDF原理图,展示了电路板布局、元件位置及电路连接关系。软件方面,代码结构清晰,模块化设计良好,包括初始化、速度数据处理、PWM配置、故障保护机制等功能模块。文中还提到了一些独特的设计细节,如PWM死区补偿、故障分级处理、卡尔曼滤波估算电池电量等。此外,代码仓库中还包括了详细的注释和调试技巧,如CAN总线实时数据传输、硬件级关断+软件状态机联动等。 适合人群:具备一定嵌入式开发基础的研发人员,尤其是对STM32F4系列单片机和电动车辆控制系统感兴趣的工程师。 使用场景及目标:适用于希望深入了解电动四轮车控制器设计原理和技术实现的研究人员和开发者。目标是掌握电动四轮车控制器的硬件设计方法和软件编程技巧,提升实际项目开发能力。 其他说明:本文不仅提供了代码和技术细节,还分享了许多实战经验和设计思路,有助于读者更好地理解和应用这些技术。

    【剧本杀AI提示词指令】基于AI的剧本杀定制化创作系统(deepseek,豆包,kimi,chatGPT,扣子空间,manus,AI训练师)

    内容概要:本文介绍了一个专业的剧本杀创作作家AI。它能根据客户需求创作各种风格和难度的剧本杀剧本,并提供创作建议和修改意见。其目标是创造引人入胜、逻辑严密的剧本体验。它的工作流程包括接收理解剧本要求、创作剧本框架情节、设计角色背景线索任务剧情走向、提供修改完善建议、确保剧本可玩性和故事连贯性。它需保证剧本原创、符合道德法律标准并在规定时间内完成创作。它具备剧本创作技巧、角色构建理解、线索悬念编织、文学知识和创意思维、不同文化背景下剧本风格掌握以及剧本杀游戏机制和玩家心理熟悉等技能。; 适合人群:有剧本杀创作需求的人群,如剧本杀爱好者、创作者等。; 使用场景及目标:①为用户提供符合要求的剧本杀剧本创作服务;②帮助用户完善剧本杀剧本,提高剧本质量。; 阅读建议:此资源详细介绍了剧本杀创作作家AI的功能和服务流程,用户可以依据自身需求与该AI合作,明确表达自己的创作需求并配合其工作流程。

    空气耦合超声仿真的COMSOL模型解析与应用实例

    内容概要:本文详细介绍了五个用于空气耦合超声仿真的COMSOL模型,涵盖二维和三维场景,适用于铝板和钢板的多种缺陷检测。每个模型都包含了具体的参数设置、边界条件选择以及优化技巧。例如,Lamb波检测模型展示了如何利用A0模态检测铝板内的气泡,而三维模型则强调了内存管理和入射角参数化扫描的重要性。表面波检测模型提供了裂纹识别的相关性分析方法,变厚度模型则展示了如何通过几何参数化来模拟复杂的工件形态。文中还分享了许多实用的操作技巧,如内存优化、信号处理和自动化检测逻辑。 适用人群:从事无损检测研究的技术人员、COMSOL软件使用者、超声检测领域的研究人员。 使用场景及目标:①帮助用户理解和掌握空气耦合超声仿真的具体实现方法;②提供实际工程应用中的缺陷检测解决方案;③指导用户进行高效的仿真建模和结果分析。 其他说明:文中提供的模型不仅涵盖了常见的缺陷检测场景,还包括了一些高级技巧,如参数化扫描、自动化检测逻辑等,能够显著提高工作效率。同时,文中还给出了硬件配置建议和一些常见的注意事项,确保用户可以顺利运行这些模型。

    【精通各种销售文案的专家】AI提示词销售文案自动生成系统:文案创作与优化全流程解析

    内容概要:本文档介绍了名为“精通各种销售文案的专家”的虚拟角色,该角色由深度学习和自然语言处理技术构建,旨在为各行业提供专业的销售文案服务。文档详细列出了角色的背景、偏好、目标、限制条件以及技能。它强调了角色在文案创意撰写、精准市场定位、效果优化和培训指导方面的能力,并且提到它能够根据不同的产品特性创作多元化的文案风格,同时确保文案符合法律规范、品牌形象一致性和时效性。此外,还展示了具体的文案示例,如智能手表和空气净化器的广告语,最后概述了与用户合作的标准流程,包括初步沟通、文案构思、初稿撰写及反馈修订等步骤。; 适合人群:需要撰写或优化销售文案的企业营销人员、广告策划师以及想要提高文案写作水平的内容创作者。; 使用场景及目标:①为企业或个人提供定制化销售文案服务,以提升品牌影响力和销售业绩;②帮助文案撰写者掌握文案策划技巧,提高文案质量;③确保文案符合法律法规和品牌要求,维护品牌形象。; 阅读建议:阅读时应重点关注角色的核心能力和所提供的具体服务,同时注意文档中提及的文案创作原则和流程,以便更好地理解如何利用该角色来满足自身的文案需求。

    【KUKA 机器人资料】:kuka Robot 初级培训.pdf

    KUKA机器人相关文档

    多智能体系统中神经网络与自适应动态滑模控制的Simulink复现及优化

    内容概要:本文详细探讨了多智能体系统中神经网络与自适应动态滑模控制的应用及其在Simulink中的复现。首先介绍了多智能体系统的概念及其通信方式,然后讨论了神经网络在多智能体决策中的应用,展示了如何使用Keras构建前馈神经网络。接着阐述了自适应动态滑模控制的基本原理,包括滑模面设计和自适应控制参数调整。最后,重点讲解了如何在Simulink中集成这些技术,提供了具体的实现步骤和优化建议,如使用Matlab Function模块嵌入神经网络和自适应滑模控制算法,以及解决抖振问题的方法。 适合人群:从事智能控制系统研究和开发的技术人员,尤其是对多智能体系统、神经网络和自适应动态滑模控制感兴趣的科研人员和工程师。 使用场景及目标:适用于需要提高多智能体系统在复杂环境下稳定性和效率的研究项目。具体目标包括减少控制系统的抖振现象,提升响应速度和精度,降低计算资源消耗。 其他说明:文中提供的代码片段和实现细节有助于读者快速理解和应用这些先进技术。同时,强调了在实际工程中需要注意的问题,如采样时间和代数环检测等。

    永磁同步电机无传感器控制:基于改进卡尔曼滤波速度观测器的Simulink建模与应用

    内容概要:本文详细探讨了永磁同步电机(PMSM)无传感器控制领域的改进卡尔曼滤波速度观测器的应用。首先介绍了卡尔曼滤波的基本原理及其在PMSM速度观测中的应用,指出了传统卡尔曼滤波在复杂非线性系统中的局限性。接着阐述了改进卡尔曼滤波的具体方法,包括自适应调整过程噪声协方差矩阵Q和观测噪声协方差矩阵R,以应对PMSM运行时参数变化的情况。文中还展示了如何在Simulink中构建PMSM的数学模型并实现普通和改进卡尔曼滤波的子模块,通过仿真对比验证了改进算法的有效性和优越性。此外,讨论了改进版在不同工况下的表现,尤其是在高速区和负载突变情况下的精度提升。 适合人群:从事电机控制系统研究与开发的技术人员,尤其是对卡尔曼滤波有一定了解并希望深入了解其在PMSM无传感器控制中应用的人群。 使用场景及目标:适用于需要提高PMSM无传感器控制精度的研发项目,目标是通过改进卡尔曼滤波算法,实现更精准的速度和位置估计,降低系统成本并提高可靠性。 其他说明:文章强调了改进卡尔曼滤波在实际应用中的细节处理,如自适应调整噪声协方差矩阵、优化矩阵运算等,为后续研究提供了宝贵的实践经验和技术指导。

    游戏型多媒体教育软件.ppt

    游戏型多媒体教育软件.ppt

    【KUKA 机器人资料】:KUKA Unternehmenspr_sentation.pdf

    KUKA机器人相关文档

Global site tag (gtag.js) - Google Analytics