lua论文 lua-an extensible extension language 中文翻译

Lua在葡萄牙语中是月亮的意思。
学习Lua的过程中翻译了这篇96年发表的论文,用作学习,还有有许多翻译不足的地方,希望讨论的小伙伴可以留言。
lua论文翻译
地址 https://www.lua.org/spe.html

lua是一个可扩展的扩展性语言
摘要
这篇论文描述了lua,一门用于扩展应用的语言。lua通过简单却强大的表机制,将程序特性和强大的数据描述能力相结合。这个机制实现了记录,数组,递归数据类型,还有面向对象的机制,例如带有动态分配的方法,等概念。lua也实现了一种允许编程者以一种不同以往的方式拓展lua语义的fallback机制。一个值得注意的例子是,回调允许使用者添加不同的的语言继承特性。目前,lua被广泛的使用在多任务生产,包括用户配置,通用目标的数据入口,用户接口描述,结构化图像元文件的存储,和通用的有限元素片段属性配置。
介绍
定制化app正呈现一个上升的趋势。当应用正在变得复杂,通过简单的元素来定制化变得不可能:用户现在希望在运行是进行配置决定;用户也希望写宏命令和脚本去提升生产力。为了实现这些需求,现在有一个很重要的趋势,就是将复杂系统分为两部分:内核和配置。内核实现了基础的类和系统的对象,并且通常由一个编译型的,静态类型的语言,像c和modula-2.配置部分,通常由交互的,灵活的语言,其连接了这些类和对象去绘制出最终的应用。

配置语言拥有多个品种,从简单的用于选择依赖,通常由命令行中的参数列表或者从配置文件中读取的键值对实现,到嵌入式语言,用于使用通过应用提供的原始元素实现的用户自定义的函数扩展app。嵌入式语言可以十分强大,有时候是一些主流编程语言,例如lisp和c的简化版。这些配置语言也被叫做扩展语言,因为他们允许基础利用新的用户定义的内容来扩展内核语义。

让扩展语言与独立语言不同的是,他只能在宿主语言中工作,称为宿主程序。此外,宿主程序通常提供领域特定的扩展去定制嵌入的语言为了宿主语言自己的目的,通常的是通过提供更高等级的抽象。为了这个,一个嵌入式语言既拥有语义为了自己的程序,又有一个应用程序api为了与宿主程序沟通。不想更简单的配置语言,那些习惯于提供参数值和系列行动给宿主程序,这由一个双向的交流在宿主程序和主程序之间。

值得注意的是,对扩展语言的需要与那些通用目的的编程语言之间是不同的。这些主要需求是:

  • 扩展语言需要好的数据描述能力,因为他们是经常被用作配置语言。
  • 配置语言应该有一个清晰和简单的语法,因为他们的主要用户不是专业的编程人员。
  • 配置语言应该是小型的,并且有一个小的实现。否则,添加这些库到一个应用的代价会变得很高。
  • 扩展语言不是用几十万行代码为软件编写大的片段。因此,那些为支持大型项目编程的机制,像静态类型检查,信息隐藏,异常处理等变得不那么重要。
  • 最后一点,扩展语言应该也是可扩展的。不想传统的语言,扩展语言被用于一个很高的抽象层次,足以与不同领域的用户交互。

这篇论文描述了Lua,一门扩展的过程是语言,拥有强大的数据描述能力,设计用于以一种通用目的的扩展语言。lua作为两种描述语言而提出,色艺用于配置两种特定的应用:一个是科学数据入口,另一个是从地址探头获得数据中进行可视化地质分析。当用户开始对这些语言逐渐要求更高的能力时,一门具备真正的编程能力的语言的要求就变得清晰了。为了不同时升级和维护两个不同的语言,被采纳的解决办法就是设计一们能够既服务于这两个应用,又可以为其他应用服务的语言。因此,Lua融合和大多数过程语言的能力,控制结构(whiles,ifs,等),声明,子程序,和中缀操作符,而且抽象出特定于任何领域的语言特性。这样,lua不仅可以用作一个完整的语言,并且也可以用作一个语言框架。

Lua很好的实现了上述的需求。他的语言和控制结构十分简单,像Pascal。Lua很小,整个类库大概6千行的ANSI C,其中大概两千行是由yacc实现的。最终,Lua是可扩展的。在他的设计中,许多不同的特性的添加已经被一些鱼汛编程者自己去实现这些特性的元机制替代了。这些元机制是:动态关联数组,反射功能,和回退。

动态关联数组直接实现了一系列的数据类型,像普通的数组,记录,sets和bags。他们通过构造器也提升了语言的数据描述能力。

反射功能允许高多态部件的创造。持久性和多命名空间是不直接在lua中实现的两个例子,但这些可以在Lua中通过反射功能简单的实现。

最后,尽管Lua有一个固定的语法,回调可以扩展许多构造的语法的意义。举例说,回调可以被用来实现不同的特性,一些不在Lua中的特性。

Lua预览
这个部分包含了Lua中主要的一些概念的简短介绍。展示了一些以示例代码展现的例子,来展现Lua这门语言的特色。完整的语言的定义可以在参考手册中找到。

Lua是一个通用目的的嵌入式编程语言,被设计用来支持过程式的编程,使用数据描述能力。作为一个嵌入式语言,Lua没有主程序的概念,它只嵌入在一个宿主上工作。Lua提供了一个C函数的库,可以被连接到主程序。主程序可以访问库中的函数去执行一段Lua代码,读和写Lua变量,并且注册c的函数,被Lua调用。另外,回调函数可以在当Lua不知道如何执行时,被调用。这种方式下,Lua可以被用于复制多个不同的领域,这样可以创建定制化的编程语言仅共享一个语法框架。这就是为什么Lua被称为一个语法框架。另一方面,为Lua写一个交互式的,独立的的翻译器十分简单。
#include <stdio.h>
#include “lua.h”
#include “lualib.h”
int main(int argc,char *argv[])
{
char line[BUFSIZ];
iolib_open();
strlib_open();
mathlib_open();
while(gets(line)!=0)
lua_dostring(line);
}
Figure1:一个Lua的交互式解释器
Lua中的所有状态都是在一个全局环境中被执行的,这个全局环境保存了所有的全局变量和函数。这个环境在主程序开始时就被初始化了,并且保存到直到主程序结束。

Lua的一个命令集合被叫做一个chunk。一个chunk可以包含状态和函数定义。当一个chunk被执行时,首先啊所有的函数和状态都被编译了,并且函数被加到全局环境中,然后这个状态被按序列执行。

Figure2展示一个Lua只如何作为一个很普通的配置语言的例子。这个代码定义了三个全局变量并且给他们夫勒支。Lua是一个动态类型语言:变量没有类型,只有值,所有的值都携带了他们的类型。这样,Lua中就没有值的类型了。
width = 420
height = width3/2
color = “blue”
Figure2:一个简单的配置文件
更多的强大的配置可以通过六控制和函数定义来编写。Lua使用了一个传统的Pascal类型的语法,使用保留字和显示终止的块,分号是可选的。这样的语法是熟悉的,健壮的,可以被很简单的分解。一个简单的例子被展示在Figure3.注意到函数可以返回多个值,和多个表达式可以被用来接受这些值。这样,通过引用传递的参数,总是一些语义上的困难,可以被从语言中去除(像c中传递多参数,需要通过结构体指针来传递)。
function Bound(w,h)
if w<20 then w = 20
elseif w>500 then w = 500
end
local minH = w
3/2
if h<minH then h = minH end
return w,h
end
width,heiht = Bound(420,500)
if monochrome then color = “black” else color = “blue” end
Figure3:使用函数的配置文件

Lua中的函数是第一类值。函数定义创造了一个值–function,并给把这个复制给了一个全局变量。跟其他类型的变脸一样,函数值可以被存储在变量中,通过参数传给其他函数或者作为返回值返回。这个特点极大的简化了面向对象的实现,在之后会提到这一特性。

除了基础的类型number 和 string 还有function,Lua提供了另外三种数据类型:nil,userdata,和table。当需要确切的类型检查时,原始函数type就会被用上,它返回了一个字符串表明这个变量的类型。

nil类型是一个单独的值,也被称为nil,他的主要的类型时和任何其他类型的值都不同。在赋初值之前,这个变量的值就是nil。这样,未初始化的变量,一个编程错误的主要来源,就不存在在lua中。使用nil在一个需要确切的值的上下文中,比如说,在一个数学表达时中,会返回一个执行错误,警告编程者这个值没有被正确的初始化。

userdata类型被提供给允许任意的主程序的数据,以void*的c指针形式呈现,存储在lua变量中。这个唯一的有效的操作这个类型的变量时赋值和判断相等。

最后,类型table实现了关联数组,就是,可以不仅仅被整数,字符串,实数,tables,和函数变量都可以索引这个数组。

关联数组
关联数组是一个强大的语言结构;许多算法在复杂度上都被简化了,因为语言提供的需要的数据结构和搜寻算法都是简化的。许多经典的数据容器像普通的数组,sets,bags和符号表,都可以直接由tables实现。tables也可以通过简单的使用名字当作切片来模拟记录。Lua通过提供a.name 作为a[“name”]的语法糖来支持这个表现。

不像其他实现了关联数组的语言,像AWK,Tcl,和Perl,Lua的tables不是绑定于变量名,相反,他们时被动态创建的对象,可以被像传统语言中的指针一样操作。这个选择的缺点时一个table必须在实现之前创建。优势是table可以自由的指向其他的tables,并且可以很好的模拟递归数据类型,和创造普通的图数据结构,可能带有循环。Figure4展示了如何在Lua中创建一个循环链表。
list = {}
current = list
i = 0
while i < 10 do
current.value = i
current.next = {}
current = current.next
i = i + 1
end
curent.value = i
current.next = list
Figure4:Lua中的循环链表

Lua提供了许多有趣的方式去创建一个表。最简单的方式是表达式{},返回了一个空表。ige更由表现力的方式是创建一个table,并且初始化了一些字段,如下所示;这个语法是被BibTex的数据库形式某种程度上启发的。
windowl = {x = 200,y = 300,foreground = “blue”}

这个命令创建了一个table,初始化了他的x,y和foreground字段,并且把它赋值给了变量windowl。table并不需要均匀;他们可以同时存储所有类型。

一个简单的语法可以被用来创建lists:
color = {“blue”,“yelloe”,“red”,“green”,“black”}

这个状态等效于:
colors = {}
colors[1] = “blue”; colors[2] = “yellow”; colors[3] = “red”
colors[4] = “green”; colors[5] = “black”

有时,需要一些更加强大的结构能力。并非通过尝试提供所有的东西,Lua提供了一个简单的构造机制。构造器被表示为name{…},这个指数name({…})的语法糖。如此,通过一个构造器,表被创建,初始化,并被变量传递给函数。这个函数可以做任意需要的初始化,例如动态类型检查,一些不存在的字段的初始化,一些辅助数据结构的更新,甚至在主程序中也可以。通常,这个构造器函数是提前在在C或Lua中定义的,并且配置用户并不知道这个构造器是一个函数,他们只是被写成类似如下的东西:
windowl = Window{x = 200,y = 300,foreignground = “blue”}
因为构造器是表岛是,他们可以被隐藏,遗址中声明的方式去描述一些更复杂的结构,像如下的代码所示:
d = dialog{
hbox{
button{lable = “ok”}
button{lable = “cancle”}
}
}

反射能力

另一个强大的Lua机制是它遍历table的能力,使用内置的函数next。这个函数需要两个变量:一个需要被遍历的table和一个表的索引。当index是nil四,这个函数返回这个表的第一个索引和其对应的值;当这个index不是nil四,这个函数返回了下一个索引和对应的值。索引以任意的顺序检索,并且一个nil的index被用来表示这个遍历的结束。一个使用Lua的遍历能力的例子是Figure5,它表示了一个克隆对象的例程。本地变量i遍历对象o的索引,而v接收他们的值。这些关联于对应索引的值,被存储在本地变量new_o.
function clone(o)
local new_o = {}
local i,v = next(o,nil)
while i do
new_o[i] = v
i,v = next(o,i)
end
return new_o
end
Figure5:克隆普通对象的函数

与用next遍历一个表的同样的方式,一个关联的函数nextvar,遍历了Lua的全局遍历。Figure6表明了一个将Lua中的全局变量保存在一张表中的函数。在函数cone中,一个本地变量n宾利了所有的全局变量的名字,而v接受了他们的值,存储在一个本地的表env。在exit中,这个函数save返回了这张表,在之后可以将这张表给restore函数去回复这个全局变量(Figure7)。这个函数由两个阶段。首先,这整个当前的环境是被清除了,包括预先定义的函数。然后本地变量n和v遍历索引和值,保存这些值在关联的全局变量中。一个取巧的方式是这些被restore函数调用的函数必须在本地变量中存储,因为所有的全局变量都被清除了。
function save()
local env = {}
local n,v = nextvar(nil)
while n do
env[n] = v
n,v = nextvar(n)
end
return env
end
Figure6:保存Lua环境的函数
function restore(env)
local nextvar,next,setglobal = nextvar,next,setglobal
local n,v = nextvar(nil)
while n do
setglobal(n,nil)
n,v = nextvar(n)
end
n,v = next(env,nil)
while n do
setglobal(n,v)
n,v = next(env,n)
end
end
Figure7:还原Lua环境的函数

尽管这是一个有趣的例子,Lua中操作全局变量的能力是几乎不被需要的,因为被作为对象使用的tables提供了一个更好的方式去维持多环境。

面向对象编程的支持

因为函数是第一类值,表的字段可以指向函数。这个特性允许一些有趣的面向对象的能力的实现,通过语法糖,定义和调用函数变得更加简单了。

首先,方法定义可以被写成
function object:methond(params)

end
这些等效于
function dummy_name(self,params)

end
object.methon = dummy_name
这样,一个匿名的函数就被创建了,并被存储在一个表的字段中,更重要的是,这个函数有一个隐藏的参数叫做self。

第二,一个函数调用可以被写作
receiver:methond(params)
这个可以翻译成
receiver.method(receiver,params)
总之,这个函数的接受者被作为函数的第一个参数传递了,赋予了self应有的意义

标注出以上构造的特性是有意义的。首先,它不提供信息隐藏。所以,纯粹主义者可能指出这个面向对象的重要特性被丢失了。第二,它不提供类;每个对象星二代了他的操作。不过,这个构造极其轻量(只有语法糖),并且类可以使用继承来模拟,就像其他以原型为基础的语言一样,像Self。但是,在讨论继承之前,有必要先讨论一下回调。

回调(Fallbacks)
作为一个没有类型的语言,Lua的语义有许多运行时异常条件。例如,与非数字进行数字运算,尝试索引一个表示table的变量,或者调用一个不是函数的变量。因为作为一个嵌入式语言,在这些情况下停止程序会不太合适,Lua运行编程者去设置他们自己的函数去处理错误条件,像这些函数称为fallback函数。回调函数也被使用于提供处理其他非严格意义上的错误情况的方法,像访问一个表中不存在的字段和标识垃圾回收

要设置一个回调函数,编程者使用一个setfallback的函数,用到两个参数,一个标识回调函数的字符串,一个用于触发条件时调用的函数。函数setfallback返回了老的回调函数,所以程序可以为不同种类的对象链接回调函数。

Lua提供了以下的回调,以如下字符串标识:
“arith",“order”,“concat”
当一个操作一个不合法的操作数时会调用这些回调函数。他们接受三个变量:两个操作数和一个标识被“冒犯”了的操作符的字符串。返回值时这个操作的最终结果。这些fallback的默认的函数是error。

”index"
当Lua尝试去获得一个不存在在表中的值时会调用这个fallback。它以这张表和索引作为参数。返回值是index操作的结果。默认返回nil

“gettable”,“settable”
Lua尝试去读或写一个非表的值会调用这些函数。默认的返回一个错误。

“function"
Lua尝试去调用一个非函数的值时会调用这个函数。它接受这个非函数值和传递的参数作为参数。返回了结果默认时error。

“gc”
在垃圾回收时调用。它接受回收的表作为参数,nil表示垃圾回收结束。默认不做任何事。

在继续之前,值得注意的事,回调回调并不经常被普通的Lua编程者们设置。回调主要由专家编程者当绑定到一个特定的应用时使用。之后,这个能力被当作语言的集成部分。一个典型的例子是,许多真实的应用使用回调函数去实现继承,像下面描述的那样。许多Lua的编程者使用继承并不知道或不关心它是怎么实现的。

使用fallback

Figure8 展示了一个使用fallback去允许一个解释二元运算符的更加面向对象风格的例子。当这个fallback被设置了,像a+b这样的表达式,a是一个table,被以a:add(b)的方式执行。胡一刀使用全局变量oldFallback去串联fallback函数。
function dispatch(reciever,parameter,operator)
if type(reciever) == “table” then
return recieveroperator
else
return oldFallback(recieve,parameter,operator)
end
end
oldFallback = sefallback(“arith”,dispatch)
Figure8:fallback的一个例子

另外一个不常用的例子是Lua的分词器的再利用。许多应用会从数字表达式分词器得利,但是不包含它,因为不是所有人都有专业知识,或者倾向去从0写一个分词器,或者使用一个像yacc一样的表达式分词器。Figure9显示了完整的使用fallback实现表达式解释器。这个程序读入一个数字表达式,输出使用变量t1,t2…作为临时变量表示需要被计算的原始操作的系列。例如,这个表达式
(aa+bb)(aa-bb)/(aa+bb+c)+(a(b*b)*c)
代码生成的结果是:
t1=mul(a,a) t2=mul(b,b) t3=add(t1,t2)
t4=sub(t1,t2) t5=mul(t3,t4) t6=add(t3,c)
t7=div(t5,t6) t8=mul(a,t2) t9=mul(t8,c)
t10=add(t7,t9)

这个程序的主要部分是这个函数arithfb,这个函数作为数学操作符的fallback。函数create用表初始化变量a,…,z,每个用name字段包含变量的名字。在初始化之后,一个循环读取数字表达式的行,为E赋值了一个表达式,并把它传给Lua解释器,叫做dostring。每次这个解释器尝试执行代码像a*a,他会调用"arith”回调,因为这个a是一个表,不是一个数字。这个回调创建了一个临时的变量去存储每个数字表达式的结果的符号表示。

尽管小,这个代码确实展现了一个全局通常的字表达式声明并生成了优化代码。注意到在上面的例子中,aa+bb和aa-bb都在aa和bb的基础上评估。也注意到aa+bb制备计算一次。代码通过存储在表T中的之前的计算结果就简单的实现了优化,被以原始的计算结果的字符表示索引。例如,T[“mul(a,a)”]是t1.

这个Figure9的代码可以被简单的修改以处理加法和乘法的交换下和除法和减法的非交换性。将其更改为后缀表示形式和其他表示形式也很简单。

在真正的应用中,变量a,…,z会代表应用对象,像复数,举证,甚至图像,这个"arith"回调会调用应用函数去执行实际的对这些对象的操作。这样,Lua分词器的主要使用是为了允许编程者去使用熟悉的数学表达式去表示对应用对象复杂的计算。

n = 0
T = {}
function arithfb(a,b,op)
	local i = op.."("..a.name..","..b.nme..")"
	if T[i]==nil then 
		n = n+1
		T[i] = create("t"..n)
		print(T[i].name..'='..i)
	end
	return T[i]
end
setfallback("arith",arithfb)
function create(v)
	local t={name=v}
	setglobal(v,t)
	return t
end
create("a") create("b") create("c") ... create("z")
while 1 do
	 local s=read()
	 if(s==nil)then exit() end
	 dostring("E="s)
	 print(s.."="E.name.."\n")
	end

Figure9:一个优化的Lua数学表达式解释器

通过fallbacks实现的继承
当然,fallbacks中的最有趣的一个用途就是Lua中实现继承。简单的继承允许一个对象到另一个对象中查找当前对象不存在的字段,叫做他的父对象,特别的,这个字段可以是一个函数。于Smalltalk和c++中的更传统的类继承相比,这个机制是一种对象继承。在Lua中实现简单继承的方式是存储他的父对象在一个可区分的字段,比如叫做parent,并且像Figure 10中所示,设置一个index fallback函数。这个代码定义了一个函数Inherit并且设置他为index fallback。当Lua尝试去访问一个不存在的字段时,这个fallback机制调用函数Inherit。这个函数首先检车这个对象是否由对象包含表值的parent字段。如果由,他尝试去获取这个想要的字段在parent字段中;这个过程会一直“向上”重复,知道一个该字段的值被找到或者这个父节点连接结束。
function Inherit(object,field)
if field == “parent” then
return nil
end
local p = object.parent
if (type§ == “table” then
return p[field]
else
return nil
end
end
setfallback(“index”,Inherit)
Figure10:Lua中简单的继承实现

上面的计划允许无数的变体。距离来说,只有方法可以继承,或者只有带有下划线的字段。fu许多多继承的形式也可以被实现。在这些方法中,有一个经常使用的形式时双继承。在这个模型中,无论何时有一个字段没在父对象中找到,将继续通过关联父对象搜寻,双继承可以模拟通常的多继承。在下方的代码中,举例来说,一个继承了a1,a2和a3的对象:
a = {parent = a1,godparent={parent=a2,godparent=a3}}

Lua在实际应用中的应用
TeCCraf是一个里约热内卢天主教大学的一个研究和发展实验室,拥有许多工业合作伙伴。在过去的两年中,TeCGraf大约有40名程序员使用Lua开发了一些重要的产品。本节描述其中的一些用途。

地址分析可配置报告生成器
在介绍中提及到,Lua最初是由两个不同的拥有他们自己的但是局限性的扩展语言的应用提出发展的。其中一个应用时一个可视化从地质探针获得的岩性数据的工具。它主要的特点时允许用户去配置这个配置文件的布局,融合一些对象的实例并且标注出要显示的数据。这个程序提供了几个对象的类型,像持续的曲线,柱状图,地质表示,缩放等。

去创建一个布局,使用者可能需要写描述这些对象的Lua代码。这个应用也需要允许通过一个图形用户接口创建的这种描述的Lua代码。这个能力描述如下,是在EDG框架的基础上建造的。
Grid{
name = “log”,
log = TRUE,
h_step = 25,
v_step = 25,
v_tick = 5,
step_line = Line(color = RED,width = SIMPLE},
tick_line = Line{color = CORAL}
}

存储结构化图像元文件
Lua的另一个重要用途是存储结构化的图形元文件。TeCGraf开发的通用绘图编辑器TeCDraw用Lua保存包含构成绘图的图形对象的高级描述的元文件。图12说明了这些描述。

line{
x = { 0.0, 1.0 },
y = { 5.0, 8.0 },
color = RED
}
text{
x = 0.8,
y = 0.5,
text = ‘an example of text’,
color = BLUE
}
circle{
x = 1.0,
y = 1.0,
r = 5.0
}
Figure12:一个结构化的图形元文件的摘录。
这种通用的结构化元文件带来了发展的几个益处
作为一个直接的结果,Lua解释器可以直接被用来加载和分析元文件;这个编辑器只提供保留Lua对象和将他们转化为对应应用的函数。
有可以分享图形对象通过使用同一个元文件的形式。更多的是,以这些应用生成的图形对象可以被TeCDraw编辑。
这个Lua语法结构化的描述让这个元文件可以人工更改:使用传统的编辑器标识和修改一个物体很简单。
既然每个物体都可以很简单的修改,这个可以独立的操作。这个特点在EDG系统中用于实现活跃的图像文件支持。
一个Lua的图像元文件允许一个过程对象的实例化。举例来说,使用数学表达式来标识曲线是可行的。

高等级,通用图像数据入口

Lua特点也在EDG系统的实现上充分利用了,这是一个支持数据入口程序发展的系统,拥有高度抽象等级。这个系统提够了接口对象(像按钮,菜单,队列)和图形对象(像线,圆,和一些基本类型)的操作。因此,编程者应该能够创造复杂的接口对话框在一个高抽象编程等级。编程者也能够联系回调操作和图形对象,这样就创造了按程序回应用户输入的动态对象。

这个EDG系统使用了Lua fallback 特点去实现双继承,如上所述。这样,新的接口和图形对象可以被构建,继承原始对象的特点。另一个有趣的EDG中继承的使用是跨语言继承。EDG实在便携式用户接口工具包IUP上创建的。为了避免在Lua IUP中复制存在主程序中的数据,EDG使用fallbacks “gettable"和 ”settable“ 去访问工具包中直接来自Lua中的字段。这样,主程序数据可以被直接访问使用一个直观记录的语法,而不用为每一个主程序中的输出数据对象创造一个访问函数。

这个EDG系统已经被用于多个数据入口程序的发展。在许多工程系统中,完整的分析被分为三个步骤:数据入口,叫做预处理;分析,叫做处理或者模拟;和结果报告和修正,叫做后处理。数据入口工作可以通过为必须指定为分析的输入的数据画图像标识变得很简单。在这些应用中,EDG系统是很有帮助的,并提供了一个很快的开发工具去定制数据入口。这些图形数据入口工具给批量模拟程序的遗留代码赋予了新生命。

有限元素网格的通用属性配置
另一个使用Lua的工程领域是有限元素网格的生成。一个有限元素网格是由节点和元素构成的,这些解构了分析的领域。去完成这个模型,物理属性必须与节点和元素连接上,像材质类型,支持条件和加载事件。一堆必须被详细说明的属性根据需要完成的分析的不同有很大的不同。这样,去实现通用的有限元素网格生成器,值得推荐的做法是保持用户可配置性,不要把代码写死在程序中。

ESAM是一个通用的系统使用Lua去提供属性配置支持。与EDG一样,ESAM采用了一个面向对象的方法:用户创造特定的从预先定义内核类中衍生的属性。Figure13展示了一个如何创建一个叫做Isotropic的新类型材质的例子。
ISO_MAT = ctrclass{parent = MATERIAL,
name = “Isotropic”,
vars = {“e”,“nu”}
}
function ISO_MAT:CrtDlg()
…创建一个对话框来指定这个材质
end
Figure13:在ESAM中创建一个新材质

相关的工作

本节讨论了一些其他的扩展语言,并且和Lua做了一些比较。并不准备面面俱到,相反,我们挑选了一些目前扩展语言发展趋势中的一些代表:Scheme,Tcl和Python。你可以在网络中找到一份全面的嵌入式语言的列表。本节也比较了fallback机制和其他语言机制。

Lisp类型的语言,尤其是Schme,是扩展语言的一个热门选择,因为他们简单,容易解析的语法和内置的扩展性。举例来说,文本编辑器Emacs的主要部分是在它自己的Lisp的变体上写的;几个其他的文本编辑器也是相同的方式。目前有许多以库的形式实现的Scheme,专门为作为嵌入式语言设计出来(例如,libscheme,OScheme,和Elk)。但是Lisp不能被称为用户友好型的,当他变得定制化。它的语法对于不是编程者来说还是很粗糙的。另外,Lisp或者Scheme的实现很少是真正可便携的。

另一个当下很出名的扩展语言是Tcl。无可置疑的是,它成功的一个重要原因是Tk的存在,一个强大的Tcl开发库,用于黄健图形用户接口。Tcl有一个很原始的语法,十分简化了他的解释器,但是也使得写一个略复杂的构造器变得很复杂。举例来说,Tcl代码计算A的两倍是A[expr $A*2].Tcl提供了一个原始的类型,string.这个事实,加上没有预编译,使得Tcl十分的低效,即使对扩展语言来说。纠正这些问题可以提升Tcl5到10的效率,按TC所示。Lua,拥有更充足的数据类型和预编译,比Tcl快10%到20%。一个简单的例子展示了调用一个不带参数的程序调用,在Tcl7.3版本运行于Sparcstation 1上,耗费了44微秒,而增加了一个全局变量花费了76微秒。在Lua2.1版本中,相同的操作分别花费了6微秒和4微秒。另一方面,Lua大约比C慢20倍。这个对于解释型语言似乎是一个典型的值。

Tcl似乎没有内置的控制结构,例如whiles和ifs。相反,控制结构通过之后的模拟是可编程的,像Smalltalk。尽管强大优雅,可编程的控制结构可以导致很神秘的程序,并且很少在实际中使用。另外,他们通常会带来表现上的不足。

Python是一个有趣的新语言,它也被称为扩展语言。但是,据它的作者说,仍然有待提上的在其他应用嵌入Python的支持,例如,重命名大多数全局变量去有Py前缀,的需求。Python不是一个小型的语言,有许多不需要在扩展语言中的特性,例如模块和错误处理。这些特点添加了应用使用这门语言的额外耗费。

Lua被设计用于融合现有语言的精华,以满足器作为可扩展扩展语言的目标。像Tcl,Lua是一个很小的库,有一个简单c的接口;这个接口是一个100行的头文件。和Tcl不同的是,Lua是一个预编译成标准二进制中间码形式。与Python类似,Lua有一个干净但熟悉的语法,和一个内置的对象的概念。与Lisp类似,Lua有一个简单数据结构机制(table),强大到可以有效的实现大多数数据结构。tables是由hash实现的。冲突是由线性查找解决的,使用一个自动的冲抵为和重hash,当table容量达到70%时。hash值被缓存以提升访问性能。

Lua中呈现的fallback机制可以被看做一种带恢复的错误处理机制。但是,Lua的动态特性允许它在许多静态语言会报编译器错误的情况下使用,上面两个例子就是这种情况。三个特定的fallback,”arith",“order”,和"concat",是主要用来实现重载的。特别的,Figure9中的例子可以被转换成带有重载的像Ada和C++的其他语言实现。但是,因为它的动态熟悉,fallback比异常处理和重载机制更加灵活。另一方面,一些作者仍未,使用了这些机制的程序比较难更改,理解和调试;这些困难在使用fallback时更加严重了。fallback应该写的小心和适度,并且仅由专业编程者书写。

结论

对配置程序越来越高的要求改变了程序的结构。现在,许多的程序由两种不同的语言书写:一个用来写强大的虚拟机,另一个用来写这个机器的单个程序。Lua是一个专门用来实现后者的程序。它很小:上文已经提到过,这整个库大概六千行ANSIC的代码。它可抑制:Lua被用于从PC-DOS到CRAY的多个平台上。它由简单的语法和一个简单的语义。并且很灵活。

尽管灵活性已经通过几个不寻常的是语言高度可扩展的机制实现了。在这些机制中,我们强调以下的几个:
关联数组是一个强大的统一数据构造器。更多的是,它比其他统一构造器,像strings和lists允许更多有效的算法。不想其他的实现关联数组的语言,Lua中的biao是动态创建的具有标识的对象。这个极大的简化了作为对象的表的使用,和面向对象功能的添加。

Fallback 允许编程者去扩展大多数内置操作符的意义。尤其是,有了fallbacks来索引操作,不同种类的继承可以被加到语言中,当arith和其他operator的fallback可以实现动态重载。

数据结构遍历的反射功能帮助生产高多态的代码。许多在其他系统中必须要由基础元素赋值的或者为每一个新类型单独赋值的操作符,在Lua中可以在一个单独的通用形式中编写。例子就是克隆对象并且操作全局环境。

为了在几个工业应用中使用Lua,我们现在在用Lua在几个研究项目中测试,从相互发送包含Lua代码的消息的分布式对象(一个Tcl此前提出的想法),到使用客户端Lua代码透明地扩展WWW浏览器。因为这些使用操作系统与Lua交互的函数都是在外部库中提供的,限制解释器的能力以此提供足够的安全很简单。

我们计划提升Lua的调试能力;目前,仅有一个简单的栈回调有用。在提供允许编程者创建属于他们自己的扩展的强大的元机制的理念下,我们计划添加简单的钩子到运行时系统去允许用户当重要事件发生时可以被通知到,比如进入或退出函数,执行一行用户代码等等。不同的调试接口可以在这些基础的钩子上构建。另外,这些狗子在创建其他工具,像性能分析器时也很有用。

本篇论文介绍的Lua的实现的在下列网址:
http://www.lua.org/ftp/lua-2.1.tar.gz

通知

我们像感谢ICAD和TeCGraf的员工,谢谢他们使用和测试Lua,和John Roll,通过邮件在早期版本的Lua发送了有关fallbacks的有价值的回馈。这个文中提到的工业应用正在PETROBRAS(巴西石油公司)和ELETROBRAS(巴西电力公司)的研究中心被共同开发。
作者的一部分赞助由巴西政府的研究和发展奖金获得。Lua在葡萄牙语中代表月亮。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值