IndiGolog程序员指南
1 域定义
1.1 在情景演算中定义理论
IndiGolog的高层程序包含原子动作和领域相关的谓词测试,一个IndiGolog程序的解释器必须能够对原子动作和谓词测试进行推理。我们在情景演算中定义所需的领域理论,情景演算是一个用来表达动态改变世界的谓词逻辑。在这种语言中,一个可能世界的历史就是一串动作,这个动作序列用一个被称作“情景”的一阶项来表示。常量So被用来表示初始状态,do(a,s)项表示在情景s中执行动作a产生的结果。
情景之间变化的关系被称为谓词流,它用最后一个参数为情景项的谓词符号来表示;例如,Holding(o,s)可能意味着机器人在情景s中正握着物体o。相似的,函数的值在不同情景间变化,也称做函数流,用]一个具有情景参数的函数符表示。特殊谓词Poss(o,s)被用来表示原子动作在情景s中是可执行的。一个应用域将被包含下面几类公理的理论来表示:
1)描述初始情景的公理So
2)动作前提公理,每个原子动作有一个动作前提公理Poss(a,s)
3)后续状态公理,每个流F都有一个后续状态公理刻画了F(x,do(a,s))在情景s下成立的条件;这些公理能从结果公理得出,但给出了一种解决框架问题的方法。
4)被感觉到的流公理,它将感觉动作返回的值和它在环境中感觉的流条件联系起来。
5)原子动作的唯一性命名公理。
6)一些功能性的领域无关公理。
1.2 下面说明的是一个领域理论如何在IndiGolog中定义:
1)描述性谓词流
prim_fluent(<name of your fluent>).
定义初始情景(你定义的一些流的取值)
initially(<name of your fluent>,<initial value>).
2)描述性原子动作
prim_action(<name of your action>).
3)定义你的动作的前提公理
poss(<name of your action>,<precondition>).
这些前提条件可能会相当复杂并包含布尔操作符“否”“与”“或”和之前定义的原子流。如果一个动作总是可以执行,那么它的前提是“真”
4)定义你的动作
execute(<name of your action>,_) :- <some code>.
注意:动作的代码是纯粹的prolog代码
5)后续状态公理定义了你的原子动作执行后流的改变
causes_val(<action>,<fluent>,<new value>,<condition>).
上面可以读作“流<fluent>的值在给定条件<condition>下执行动作<action>后变为<new value>。”
如果几个流在动作执行后改变了它们的值,你必须写一些”cause_val”声明。同样,如果没有条件,只需写”true”
除领域理论外,一个IndiGolog程序同样包括一个定义主体或机器人行为的程序部分。
2 行为定义
2.1 高层构造子
这里有一系列高层的IndiGolog构建子用来书写程序。A和B代表程序,它们可以是单个原子动做也可以是原子动作或程序的序列。在IndiGolog中,序列需要被包含在方括号中。X代表一个单独的变量或一序列变量(用方括号包含)。
1)no_op空动作
2)A,B——顺序动作
3)?(<condition>)——测试条件是否满足
4)if(<condition>,A,B)——如果条件<condition>是真,则执行A,否则执行B。
注意:no_op能够代替A或B。
5)whiel(<condition>,A)——while循环。当<condition>是真的时候执行A。
6)proc(<name of procedure>,<body of procedure>).——procedure定义。程序可以被参数化,所以我们可以写一些proc(foo(a,b), [action1, action2])这样的程序。
7)<name of procedure>(<parameters>) or <name of procedure>——程序调用
8)ndet(A,B)——非确定性选择。不确定地在程序A和程序B中间选择。
9)pi(X,A)——非确定性选择参数。不确定地为变量X选择一个绑定并执行程序A。
10)star(A)——非确定循环。执行A零次或更多次。
11)conc(A,B)——并发执行。并发地执行程序A和B。
12)pconc(A,B)——带有优先级的并发。程序A比B有更高的优先级。
并发注解:当一个进程遇到一个前提为假的原子动作或者测试动作?(<cond>)为假可能被阻塞。于是,程序的执行可能由另一个进程继续执行。在有优先级的并发中,低优先级的进程只能在高优先级的进程结束或阻塞后才能执行。
13)Iconc(A)——并发循环。就像非确定的循环star(A),但是A的实例是并发执行而不是顺序。
Interrupt(<condition>,<body>)——中断。如果中断从更高优先级的进程获得控制权,并且<condition>是真,则中断被触发并且它的主体被执行。
14)Interrupt(X,<condition>,<body>)——中断。如果中断从更高优先级的进程获得控制权并且对于绑定的变量<condition>是真,则中断被触发并且他的主体被执行。
一旦主体执行完毕,中断可能被重新触发。
15)priortized_interrupts(<list of interrupt>)——带优先级的中断。中断根据它们的优先级排列——第一个中断有最高优先级,以此类推。
这个构造子在编写控制和反应程序时常常使用。你能够把这个操作想象为一个事件循环:当prioritized_interrups获得控制权,它保持执行。开始的时候,第一个中断子句被检查,如果条件满足则执行,然后第二个中断被检查和执行,以此类推。最高优先级的中断需要立即响应。较低优先级的中断只有当更高优先级的中断检查执行完后才能被检查和执行。当然,条件可以被设置为“真”于是中断每次都被触发。
16)search(A)——IndiGolog程序默认以在线方式执行:所有的非确定选择被随机处理,任何被选择的动作都立即执行。另一方面,一个在search中的程序以离线方式执行。解释器必须在程序A被真正执行前发现一个动作序列,该序列组成程序A的一个合法的执行。
为说明在线和离线执行的差别,我们给出一个例子:
ndet(a,b), A, ?(c)
这里a和b是原子动做,A是一个程序,c是一些条件。IndiGolog解释器执行这个程序的默认方式随机选择a或b,然后执行A且测试c是否成立。这时也许发生执行a然后执行A导致c不真,于是导致程序失败。另一方面,如果我们把上述程序放在search中,解释器将首先查找一个成功的执行,然后执行它。这样我们能保证发现一个成功的执行如果它存在的话。
在一个动作序列在search块中被找到,它需要持续地被检查是否仍然可行。因为IndiGolog支持外部动作(exogenous action)。如果之前发现的动作序列不再可行就需要重新规划。
IndiGolog支持相当复杂的布尔条件。二元布尔操作符如“与”“或”和一元操作符“否”一样能被使用。所有的操作符必须以前缀方式表达。
除了上面的操作,IndiGolog还支持一些等同于存在量词的构造子。
Some(X,<boolean expression>)——X是一个单独的变量或一列变量。当变量X存在一个绑定的时候为真,这让该布尔表达式为真。
注意:IndiGolog中的变量在如pi,some和一种中断的构造子中引入,以小写字母开头,不像Prolog变量那样已大写字母开头。
当前的域理论必须在Prolog中表达,所以解释器依赖于动态的闭世界假设,即假设任何需要测试的时候,在线的解释器都具有对该问题的流地完整知识来判断该测试而不必进行基于案例等形式的推理。
IndiGolog的一个非常重要的特性是它支持感觉动作和外部事件。
2.2 感应动作
感觉动作被设计来获取/更新特定流的值。这些值能被计算或从其它主体或感应器接收。感觉动作通过下面的方式进行定义:
prim_action(<sensing action name>).——通常的动作声明
poss(<sensing action name>,<condition>).——通常的前提公理
execute(<sensing action name>, X):-<sensing action body>.——这里X是一个动作中的变量,该变量用来连接动作的执行后感应到的结果。
senses(<sensing action name>,<name of the fluent this action updates>).——这个声明将感应动作和它更新的流链接起来。
注意:感应动作不能用在search构造子中。
2.3 外部事件
主体在一个动态世界中和其它主体共存。其它主体执行的动作可以改变环境,于是主体必须尝试探测到世界中已经发生的外部事件。IndiGolog提供了一种监控例程的定义方式来探测外部事件。这种监控例程(给出了它们满足的前提公理)在每个原子动作之前尝试执行。一个外部动作在它的监控例程成功之后才考虑执行。在这里特定外部动作的后续状态的公理将告诉解释器那个流的值将要改变。
1)exog_action(<name of exo action>).——声明外部动作。
2)exog_occurs(<name of exo action>):-<body of exo action>.——定义监控例程。监控例程的主体用Prolog语言编写。
3)poss(<name of exo action>,<condition>).——前提公理。通常外部动作的条件是“真”,但是有时暂停或取消外部事件的探测例程是很有用的,于是外部动作的条件就不是无关紧要的了。
4)cause_val(<name of exo action>,<fluent name>,<new value>,<condition>).——这定义了fluent <fluent name>在外部动作发生后值的改变。
3 开发主体
3.1 一般主体模式
IndiGolog OAA agent may be viewed as an agent that executes its IndiGolog control routine and additionally has access to other OAA agents in the system and is capable of reacting to the incoming OAA messages.
IndiGolog OAA主体可被看作一个执行IndiGolog控制例程的主体,它可以访问系统中的其它OAA主体,还可以对收到的OAA消息作出反应。
This is the general pattern of changing the state of the agent in response to incoming OAA events. The reactions to these events can usually be programmed using interrupts. The main IndiGolog program may then look like this:
这是根据收到的OAA事件改变主体的状态的普遍模式。对这些事件的反应通常能够用中断来编程实现。一个主要的IndiGolog程序看起来如下所示:
proc(control,
[
<probably, some initialization>
prioritized_interrupts
([
interrupt(<check the value of the fluent that changes
when some very important OAA event arrives>,
<do something to respond>),
interrupt(<check the value of the fluent that changes
when some less important OAA event arrives>,
<do something to respond>),
interrupt(true, <do something - normal execution>)
])
]).
The first two interrupts are then the reactive part of the program, while the third one is the proactive part.
开头的两个中断是程序的外部响应部分,第三个中断是自主执行部分。
If we follow the above process, then writing an IndiGolog agent that is both proactive and reactive involves:
如果我们按照上面的步骤,写出来的IndiGolog主体就是即可自主执行也可响应执行的。
Declaring the needed procedure solvables using
声明需要的可解程序用
initial_solvables(<solvables list>)
Declaring the corresponding exogenous actions. The declared exogenous actions should be the exact copies of the procedure solvables. For example, if we declare the solvable:
声明对应的外部动作。已经声明的外部动作应该是可解程序的精确拷贝。例如,如果我们声明可解如下
solvable(update_status(New_Status), [], [])
于是我们将需要如下外部动作:
exog_action(update_status(New_Status)).
声明流,流的值当OAA事件到达时将要改变。例如:
prim_fluent(status).
写下和接收到的事件相关的后续状态公理及相应的流。例如:
causes_val(update_status(New_Status),status,New_Status,true).
用上面叙述的中断写一个主程序,命名为’control’。
Specifying the name of the agent using
定义主体的名字用
agent_name(<name>)
3.2 多文件声明
为了构造一个可执行的主体程序,需要编译Prolog源文件然后用IndiGolog解释器、IndiGolog-OAA接口和OAA库进行链接。
为了让这个过程顺利完成,主体的源文件必须用下面的多文件声明开始:
:- multifile prim_action/1, prim_fluent/1, causes_val/4, poss/2, proc/2, exog_action/1.
它应该是文件的第一行。声明一个多文件谓词意味着它的子句能够分布在多个不同的文件中。这里有一个真实的例子。
3.3 编译所需要的文件
注意:下面的程序可用于Unix的Quintus Prolog。这些脚本可能不能在MS Windows下运行。
要成功编译一个IndiGolog OAA本体需要如下这些文件:
<name of your program>.pl — The source code for the agent.
<name of your program>.pl——主体的源代码
interface.pl — The IndiGolog-OAA interface file (rename to ".pl").
interface.pl——IndiGolog-OAA接口文件
interpreter_m.pl — The IndiGolog interpreter (rename to ".pl").
interpreter_m.pl——IndiGolog解释器
oaa.pl — The OAA Prolog library.
oaa.pl——OAA Prolog库
com_tcp.pl — The OAA communication library.
com_tcp.pl——OAA通讯库
Each of the files needs to be compiled using the following command line:
每个文件都需要用下面的命令行编译:
qpc -c <name of the file>.pl
This will generate the file in the Quintus Object Format with the same name and with extension 'qof'.
这将生成同名的以“qof”为后缀的Quintus对象格式文件
然后用下面的命令将对象文件被链接到一起:
qld -d -S -lsocket -lnsl -o <desired name for the application> <name of your program>.qof interface.qof interpreter_m.qof oaa.qof com_tcp.qof
这将生成一个被命名的可执行文件。
---------------------------------------------------------------------------------------------------------
本文大部分素材取自 http://www.cs.toronto.edu/~alexei/ig-oaa/index.htm
作者主要对其进行了翻译和整理
1.1 在情景演算中定义理论
IndiGolog的高层程序包含原子动作和领域相关的谓词测试,一个IndiGolog程序的解释器必须能够对原子动作和谓词测试进行推理。我们在情景演算中定义所需的领域理论,情景演算是一个用来表达动态改变世界的谓词逻辑。在这种语言中,一个可能世界的历史就是一串动作,这个动作序列用一个被称作“情景”的一阶项来表示。常量So被用来表示初始状态,do(a,s)项表示在情景s中执行动作a产生的结果。
情景之间变化的关系被称为谓词流,它用最后一个参数为情景项的谓词符号来表示;例如,Holding(o,s)可能意味着机器人在情景s中正握着物体o。相似的,函数的值在不同情景间变化,也称做函数流,用]一个具有情景参数的函数符表示。特殊谓词Poss(o,s)被用来表示原子动作在情景s中是可执行的。一个应用域将被包含下面几类公理的理论来表示:
1)描述初始情景的公理So
2)动作前提公理,每个原子动作有一个动作前提公理Poss(a,s)
3)后续状态公理,每个流F都有一个后续状态公理刻画了F(x,do(a,s))在情景s下成立的条件;这些公理能从结果公理得出,但给出了一种解决框架问题的方法。
4)被感觉到的流公理,它将感觉动作返回的值和它在环境中感觉的流条件联系起来。
5)原子动作的唯一性命名公理。
6)一些功能性的领域无关公理。
1.2 下面说明的是一个领域理论如何在IndiGolog中定义:
1)描述性谓词流
prim_fluent(<name of your fluent>).
定义初始情景(你定义的一些流的取值)
initially(<name of your fluent>,<initial value>).
2)描述性原子动作
prim_action(<name of your action>).
3)定义你的动作的前提公理
poss(<name of your action>,<precondition>).
这些前提条件可能会相当复杂并包含布尔操作符“否”“与”“或”和之前定义的原子流。如果一个动作总是可以执行,那么它的前提是“真”
4)定义你的动作
execute(<name of your action>,_) :- <some code>.
注意:动作的代码是纯粹的prolog代码
5)后续状态公理定义了你的原子动作执行后流的改变
causes_val(<action>,<fluent>,<new value>,<condition>).
上面可以读作“流<fluent>的值在给定条件<condition>下执行动作<action>后变为<new value>。”
如果几个流在动作执行后改变了它们的值,你必须写一些”cause_val”声明。同样,如果没有条件,只需写”true”
除领域理论外,一个IndiGolog程序同样包括一个定义主体或机器人行为的程序部分。
2 行为定义
2.1 高层构造子
这里有一系列高层的IndiGolog构建子用来书写程序。A和B代表程序,它们可以是单个原子动做也可以是原子动作或程序的序列。在IndiGolog中,序列需要被包含在方括号中。X代表一个单独的变量或一序列变量(用方括号包含)。
1)no_op空动作
2)A,B——顺序动作
3)?(<condition>)——测试条件是否满足
4)if(<condition>,A,B)——如果条件<condition>是真,则执行A,否则执行B。
注意:no_op能够代替A或B。
5)whiel(<condition>,A)——while循环。当<condition>是真的时候执行A。
6)proc(<name of procedure>,<body of procedure>).——procedure定义。程序可以被参数化,所以我们可以写一些proc(foo(a,b), [action1, action2])这样的程序。
7)<name of procedure>(<parameters>) or <name of procedure>——程序调用
8)ndet(A,B)——非确定性选择。不确定地在程序A和程序B中间选择。
9)pi(X,A)——非确定性选择参数。不确定地为变量X选择一个绑定并执行程序A。
10)star(A)——非确定循环。执行A零次或更多次。
11)conc(A,B)——并发执行。并发地执行程序A和B。
12)pconc(A,B)——带有优先级的并发。程序A比B有更高的优先级。
并发注解:当一个进程遇到一个前提为假的原子动作或者测试动作?(<cond>)为假可能被阻塞。于是,程序的执行可能由另一个进程继续执行。在有优先级的并发中,低优先级的进程只能在高优先级的进程结束或阻塞后才能执行。
13)Iconc(A)——并发循环。就像非确定的循环star(A),但是A的实例是并发执行而不是顺序。
Interrupt(<condition>,<body>)——中断。如果中断从更高优先级的进程获得控制权,并且<condition>是真,则中断被触发并且它的主体被执行。
14)Interrupt(X,<condition>,<body>)——中断。如果中断从更高优先级的进程获得控制权并且对于绑定的变量<condition>是真,则中断被触发并且他的主体被执行。
一旦主体执行完毕,中断可能被重新触发。
15)priortized_interrupts(<list of interrupt>)——带优先级的中断。中断根据它们的优先级排列——第一个中断有最高优先级,以此类推。
这个构造子在编写控制和反应程序时常常使用。你能够把这个操作想象为一个事件循环:当prioritized_interrups获得控制权,它保持执行。开始的时候,第一个中断子句被检查,如果条件满足则执行,然后第二个中断被检查和执行,以此类推。最高优先级的中断需要立即响应。较低优先级的中断只有当更高优先级的中断检查执行完后才能被检查和执行。当然,条件可以被设置为“真”于是中断每次都被触发。
16)search(A)——IndiGolog程序默认以在线方式执行:所有的非确定选择被随机处理,任何被选择的动作都立即执行。另一方面,一个在search中的程序以离线方式执行。解释器必须在程序A被真正执行前发现一个动作序列,该序列组成程序A的一个合法的执行。
为说明在线和离线执行的差别,我们给出一个例子:
ndet(a,b), A, ?(c)
这里a和b是原子动做,A是一个程序,c是一些条件。IndiGolog解释器执行这个程序的默认方式随机选择a或b,然后执行A且测试c是否成立。这时也许发生执行a然后执行A导致c不真,于是导致程序失败。另一方面,如果我们把上述程序放在search中,解释器将首先查找一个成功的执行,然后执行它。这样我们能保证发现一个成功的执行如果它存在的话。
在一个动作序列在search块中被找到,它需要持续地被检查是否仍然可行。因为IndiGolog支持外部动作(exogenous action)。如果之前发现的动作序列不再可行就需要重新规划。
IndiGolog支持相当复杂的布尔条件。二元布尔操作符如“与”“或”和一元操作符“否”一样能被使用。所有的操作符必须以前缀方式表达。
除了上面的操作,IndiGolog还支持一些等同于存在量词的构造子。
Some(X,<boolean expression>)——X是一个单独的变量或一列变量。当变量X存在一个绑定的时候为真,这让该布尔表达式为真。
注意:IndiGolog中的变量在如pi,some和一种中断的构造子中引入,以小写字母开头,不像Prolog变量那样已大写字母开头。
当前的域理论必须在Prolog中表达,所以解释器依赖于动态的闭世界假设,即假设任何需要测试的时候,在线的解释器都具有对该问题的流地完整知识来判断该测试而不必进行基于案例等形式的推理。
IndiGolog的一个非常重要的特性是它支持感觉动作和外部事件。
2.2 感应动作
感觉动作被设计来获取/更新特定流的值。这些值能被计算或从其它主体或感应器接收。感觉动作通过下面的方式进行定义:
prim_action(<sensing action name>).——通常的动作声明
poss(<sensing action name>,<condition>).——通常的前提公理
execute(<sensing action name>, X):-<sensing action body>.——这里X是一个动作中的变量,该变量用来连接动作的执行后感应到的结果。
senses(<sensing action name>,<name of the fluent this action updates>).——这个声明将感应动作和它更新的流链接起来。
注意:感应动作不能用在search构造子中。
2.3 外部事件
主体在一个动态世界中和其它主体共存。其它主体执行的动作可以改变环境,于是主体必须尝试探测到世界中已经发生的外部事件。IndiGolog提供了一种监控例程的定义方式来探测外部事件。这种监控例程(给出了它们满足的前提公理)在每个原子动作之前尝试执行。一个外部动作在它的监控例程成功之后才考虑执行。在这里特定外部动作的后续状态的公理将告诉解释器那个流的值将要改变。
1)exog_action(<name of exo action>).——声明外部动作。
2)exog_occurs(<name of exo action>):-<body of exo action>.——定义监控例程。监控例程的主体用Prolog语言编写。
3)poss(<name of exo action>,<condition>).——前提公理。通常外部动作的条件是“真”,但是有时暂停或取消外部事件的探测例程是很有用的,于是外部动作的条件就不是无关紧要的了。
4)cause_val(<name of exo action>,<fluent name>,<new value>,<condition>).——这定义了fluent <fluent name>在外部动作发生后值的改变。
3 开发主体
3.1 一般主体模式
IndiGolog OAA agent may be viewed as an agent that executes its IndiGolog control routine and additionally has access to other OAA agents in the system and is capable of reacting to the incoming OAA messages.
IndiGolog OAA主体可被看作一个执行IndiGolog控制例程的主体,它可以访问系统中的其它OAA主体,还可以对收到的OAA消息作出反应。
This is the general pattern of changing the state of the agent in response to incoming OAA events. The reactions to these events can usually be programmed using interrupts. The main IndiGolog program may then look like this:
这是根据收到的OAA事件改变主体的状态的普遍模式。对这些事件的反应通常能够用中断来编程实现。一个主要的IndiGolog程序看起来如下所示:
proc(control,
[
<probably, some initialization>
prioritized_interrupts
([
interrupt(<check the value of the fluent that changes
when some very important OAA event arrives>,
<do something to respond>),
interrupt(<check the value of the fluent that changes
when some less important OAA event arrives>,
<do something to respond>),
interrupt(true, <do something - normal execution>)
])
]).
The first two interrupts are then the reactive part of the program, while the third one is the proactive part.
开头的两个中断是程序的外部响应部分,第三个中断是自主执行部分。
If we follow the above process, then writing an IndiGolog agent that is both proactive and reactive involves:
如果我们按照上面的步骤,写出来的IndiGolog主体就是即可自主执行也可响应执行的。
Declaring the needed procedure solvables using
声明需要的可解程序用
initial_solvables(<solvables list>)
Declaring the corresponding exogenous actions. The declared exogenous actions should be the exact copies of the procedure solvables. For example, if we declare the solvable:
声明对应的外部动作。已经声明的外部动作应该是可解程序的精确拷贝。例如,如果我们声明可解如下
solvable(update_status(New_Status), [], [])
于是我们将需要如下外部动作:
exog_action(update_status(New_Status)).
声明流,流的值当OAA事件到达时将要改变。例如:
prim_fluent(status).
写下和接收到的事件相关的后续状态公理及相应的流。例如:
causes_val(update_status(New_Status),status,New_Status,true).
用上面叙述的中断写一个主程序,命名为’control’。
Specifying the name of the agent using
定义主体的名字用
agent_name(<name>)
3.2 多文件声明
为了构造一个可执行的主体程序,需要编译Prolog源文件然后用IndiGolog解释器、IndiGolog-OAA接口和OAA库进行链接。
为了让这个过程顺利完成,主体的源文件必须用下面的多文件声明开始:
:- multifile prim_action/1, prim_fluent/1, causes_val/4, poss/2, proc/2, exog_action/1.
它应该是文件的第一行。声明一个多文件谓词意味着它的子句能够分布在多个不同的文件中。这里有一个真实的例子。
3.3 编译所需要的文件
注意:下面的程序可用于Unix的Quintus Prolog。这些脚本可能不能在MS Windows下运行。
要成功编译一个IndiGolog OAA本体需要如下这些文件:
<name of your program>.pl — The source code for the agent.
<name of your program>.pl——主体的源代码
interface.pl — The IndiGolog-OAA interface file (rename to ".pl").
interface.pl——IndiGolog-OAA接口文件
interpreter_m.pl — The IndiGolog interpreter (rename to ".pl").
interpreter_m.pl——IndiGolog解释器
oaa.pl — The OAA Prolog library.
oaa.pl——OAA Prolog库
com_tcp.pl — The OAA communication library.
com_tcp.pl——OAA通讯库
Each of the files needs to be compiled using the following command line:
每个文件都需要用下面的命令行编译:
qpc -c <name of the file>.pl
This will generate the file in the Quintus Object Format with the same name and with extension 'qof'.
这将生成同名的以“qof”为后缀的Quintus对象格式文件
然后用下面的命令将对象文件被链接到一起:
qld -d -S -lsocket -lnsl -o <desired name for the application> <name of your program>.qof interface.qof interpreter_m.qof oaa.qof com_tcp.qof
这将生成一个被命名的可执行文件。
---------------------------------------------------------------------------------------------------------
本文大部分素材取自 http://www.cs.toronto.edu/~alexei/ig-oaa/index.htm
作者主要对其进行了翻译和整理