简单地说一个__import__()可能不大清楚。现在就来看一个最简单的插件式结构程序。它会扫描plugins文件夹下的所有.py文件。然后把它们载入。
#-*- encoding: utf-8 -*-#main1.pyimport osclass Platform:
def __init__(self):
self.loadPlugins()
def sayHello(self, from_):
print "hello from %s." % from_
def loadPlugins(self):
for filename in os.listdir("plugins"):
if not filename.endswith(".py") or filename.startswith("_"):
continue
self.runPlugin(filename)
def runPlugin(self, filename):
pluginName=os.path.splitext(filename)[0]
plugin=__import__("plugins."+pluginName, fromlist=[pluginName])
#Errors may be occured. Handle it yourself.
plugin.run(self)if __name__=="__main__":
platform=Platform()
然后在plugins子目录燃凯里面放入两个文件:
#plugins1.pydef run(platform):
platform.sayHello("plugin1")#plugins2.pydef run(platform):
platform.sayHello("plugin2")
再创建一个空的__init__.py在plugins文件夹里面。从package里面导入模块的时候,Python要求一个__init__.py。
运行main1.py,看一下运行的结果。首先是打印一下文件夹结构方便大家理解:
h:\projects\workon\testplugins>tree /f /a
卷 Data 的文件夹 PATH 列表
卷序列号为 ****-****
H:.
| main1.py
|
\---plugins
plugin1.py
plugin2.py
__init__.py
h:\projects\workon\testplugins>main1.py
hello from plugin1.
hello from plugin2.
一般镇段中地,载入插件前要首先扫描插件,然后依次载入并运行插件。我们上面的示例程序main1.py也是如此,分为两个函数。第一个loadPlugins()扫描插件。它把plugins目录下面所有.py的文件除了__init__.py都当成插件。runPlugin()载入并运行插件。其中两个关键:使用__import__()函数把插件当成模块导入,它要求所有的插件都定义一个run()函数。各种语言实现的插件式结构其实也基本上分为这两个步骤。所不同的是,Python语言实现起来更加的简洁。
或许听起来还有点玄奥。详细地说一下__import__()。它和常见的import语句很相似,只不过换成函数形式并且返回模块以供调用。import module相当于__import__("module"),from module import func相当于__import__("module", fromlist=["func"]),不过与御山想象有点不同,import package.module相当于__import__("package.module", fromlist=["module"])。
如何调用插件一般有个约定。像我们这里就约定每个插件都实现一个run()。有时候还可以约定实现一个类,并且要求这个类实现某个管理接口,以方便核心随时启动、停止插件。要求所有的插件都有这几个接口方法:
#interfaces.pyclass Plugin:
def setPlatform(self, platform):
self.platform=platform
def start(self):
pass
def stop(self):
pass
想要运行这个插件,我们的runPlugin()要改一改,另外增加一个shutdown()来停止插件:
class Platform:
def __init__(self):
self.plugins=[]
self.loadPlugins()
def sayHello(self, from_):
print "hello from %s." % from_
def loadPlugins(self):
for filename in os.listdir("plugins"):
if not filename.endswith(".py") or filename.startswith("_"):
continue
self.runPlugin(filename)
def runPlugin(self, filename):
pluginName=os.path.splitext(filename)[0]
plugin=__import__("plugins."+pluginName, fromlist=[pluginName])
clazz=plugin.getPluginClass()
o=clazz()
o.setPlatform(self)
o.start()
self.plugins.append(o)
def shutdown(self):
for o in self.plugins:
o.stop()
o.setPlatform(None)
self.plugins=[]if __name__=="__main__":
platform=Platform()
platform.shutdown()
插件改成这样:
#plugins1.pyclass Plugin1:
def setPlatform(self, platform):
self.platform=platform
def start(self):
self.platform.sayHello("plugin1")
def stop(self):
self.platform.sayGoodbye("plugin1")def getPluginClass():
return Plugin1#plugins2.pydef sayGoodbye(self, from_):
print "goodbye from %s." % from_class Plugin2:
def setPlatform(self, platform):
self.platform=platform
if platform is not None:
platform.__class__.sayGoodbye=sayGoodbye
def start(self):
self.platform.sayHello("plugin2")
def stop(self):
self.platform.sayGoodbye("plugin2")def getPluginClass():
return Plugin2
运行结果:
h:\projects\workon\testplugins>main.py
hello from plugin1.
hello from plugin2.
goodbye from plugin1.
goodbye from plugin2.
详细观察的朋友们可能会发现,上面的main.py,plugin1.py, plugin2.py干了好几件令人惊奇的事。
首先,plugin1.py和plugin2.py里面的插件类并没有继承自interfaces.Plugin,而platform仍然可以直接调用它们的start()和stop()方法。这件事在Java、C++里面可能是件麻烦的事情,但是在Python里面却是件稀疏平常的事,仿佛吃饭喝水一般正常。事实上,这正是Python鼓励的约定编程。Python的文件接口协议就只规定了read(), write(), close()少数几个方法。多数以文件作为参数的函数都可以传入自定义的文件对象,只要实现其中一两个方法就行了,而不必实现一个什么FileInterface。如果那样的话,需要实现的函数就多了,可能要有十几个。
再仔细看下来,getPluginClass()可以把类型当成值返回。其实不止是类型,Python的函数、模块都可以被当成普通的对象使用。从类型生成一个实例也很简单,直接调用clazz()就创建一个对象。不仅如此,Python还能够修改类型。上面的例子我们就演示了如何给Platform增加一个方法。在两个插件的stop()里面我们都调用了sayGoodbye(),但是仔细观察Platform的定义,里面并没有定义。原理就在这里:
#plugins2.pydef sayGoodbye(self, from_):
print "goodbye from %s." % from_class Plugin2:
def setPlatform(self, platform):
self.platform=platform
if platform is not None:
platform.__class__.sayGoodbye=sayGoodbye
这里首先通过platform.__class__得到Platform类型,然后Platform.sayGoodbye=sayGoodbye新增了一个方法。使用这种方法,我们可以让插件任意修改核心的逻辑。这正在文首所说的Python实现插件式结构的灵活性,是静态语言如C++、Java等无法比拟的。当然,这只是演示,我不大建议使用这种方式,它改变了核心的API,可能会给其它程序员造成困惑。但是可以采用这种方式替换原来的方法,还可以利用“面向切面编程”,增强系统的功能。
接下来我们还要再改进一下载入插件的方法,或者说插件的布署方法。前面我们实现的插件体系主要的缺点是每个插件只能有一个源代码。如果想附带一些图片、声音数据,又怕它们会和其它的插件冲突。即使不冲突,下载时分成单独的文件也不方便。最好是把一个插件压缩成一个文件供下载安装。
Firefox是一个支持插件的著名软件。它的插件以.xpi作为扩展名,实际上是一个.zip文件,里面包含了javascript代码、数据文件等很多内容。它会把插件包下载复制并解压到%APPDATA%\Mozilla\Firefox\Profiles\XXXX.default\extensions里面,然后调用其中的install.js安装。与此类似,实用的Python程序也不大可能只有一个源代码,也要像Firefox那样支持.zip包格式。
实现一个类似于Firefox那样的插件布署体系并不会很难,因为Python支持读写.zip文件,只要写几行代码来做压缩与解压缩就行了。首先要看一下zipfile这个模块。用它解压缩的代码如下:
import zipfile, osdef installPlugin(filename):
with zipfile.ZipFile(filename) as pluginzip:
subdir=os.path.splitext(filename)[0]
topath=os.path.join("plugins", subdir)
pluginzip.extractall(topath)
ZipFile.extractall()是Python 2.6后新增的函数。它直接解压所有压缩包内的文件。不过这个函数只能用于受信任的压缩包。如果压缩包内包含了以/或者盘符开始的绝对路径,很有可能会损坏系统。推荐看一下zipfile模块的说明文档,事先过滤非法的路径名。
这里只有解压缩的一小段代码,安装过程的界面交互相关的代码很多,不可能在这里举例说明。我觉得UI是非常考验软件设计师的部分。常见的软件会要求用户到网站上查找并下载插件。而Firefox和KDE提供了一个“组件(部件)管理界面”,用户可以直接在界面内查找插件,查看它的描述,然后直接点击安装。安装后,我们的程序遍历插件目录,载入所有的插件。一般地,软件还需要向用户提供插件的启用、禁用、依赖等功能,甚至可以让用户直接在软件界面上给插件评分,这里就不再详述了。
有个小技巧,安装到plugins/subdir下的插件可以通过__file__得到它自己的绝对路径。如果这个插件带有图片、声音等数据的时候,可以利用这个功能载入它们。比如上面的plugin1.py这个插件,如果它想在启动的时候播放同目录的message.wav,可以这样子:
#plugins1.pyimport osdef alert():
soundFile=os.path.join(os.path.dirname(__file__), "message.wav")
try:
import winsound
winsound.PlaySound(soundFile, winsound.SND_FILENAME)
except (ImportError, RuntimeError):
passclass Plugin1:
def setPlatform(self, platform):
self.platform=platform
def start(self):
self.platform.sayHello("plugin1")
alert()
def stop(self):
self.platform.sayGoodbye("plugin1")def getPluginClass():
return Plugin1
接下来我们再介绍一种Python/Java语言常用的插件管理方式。它不需要事先有一个插件解压过程,因为Python支持从.zp文件导入模块,很类似于Java直接从.jar文件载入代码。所谓安装,只要简单地把插件复制到特定的目录即可,Python代码自动扫描并从.zip文件内载入代码。下面是一个最简单的例子,它和上面的几个例子一样,包含一个main.py,这是主程序,一个plugins子目录,用于存放插件。我们这里只有一个插件,名为plugin1.zip。plugin1.zip有以下两个文件,其中description.txt保存了插件内的入口函数和插件的名字等信息,而plugin1.py是插件的主要代码:
description.txt
plugin1.py
其中description.txt的内容是:
[general]name=plugin1description=Just a test code=plugin1.Plugin1
plugin1.py与前面的例子类似,为了省事,我们去掉了stop()方法,它的内容是:
class Plugin1:
def setPlatform(self, platform):
self.platform=platform
def start(self):
self.platform.sayHello("plugin1")
重写的main.py的内容是:
# -*- coding: utf-8 -*-import os, zipfile, sys, ConfigParserclass Platform:
def __init__(self):
self.loadPlugins()
def sayHello(self, from_):
print "hello from %s." % from_
def loadPlugins(self):
for filename in os.listdir("plugins"):
if not filename.endswith(".zip"):
continue
self.runPlugin(filename)
def runPlugin(self, filename):
pluginPath=os.path.join("plugins", filename)
pluginInfo, plugin = self.getPlugin(pluginPath)
print "loading plugin: %s, description: %s" % \(pluginInfo["name"], pluginInfo["description"])
plugin.setPlatform(self)
plugin.start()
def getPlugin(self, pluginPath):
pluginzip=zipfile.ZipFile(pluginPath, "r")
description_txt=pluginzip.open("description.txt")
parser=ConfigParser.ConfigParser()
parser.readfp(description_txt)
pluginInfo={}
pluginInfo["name"]=parser.get("general", "name")
pluginInfo["description"]=parser.get("general", "description")
pluginInfo["code"]=parser.get("general", "code")
sys.path.append(pluginPath)
moduleName, pluginClassName=pluginInfo["code"].rsplit(".", 1)
module=__import__(moduleName, fromlist=[pluginClassName, ])
pluginClass=getattr(module, pluginClassName)
plugin=pluginClass()
return pluginInfo, pluginif __name__=="__main__":
platform=Platform()
与前一个例子的主要不同之处是getPlugin()。它首先从.zip文件内读取描述信息,然后把这个.zip文件添加到sys.path里面。最后与前面类似地导入模块并执行。
解压还是不解压,两种方案各有优劣。一般地,把.zip文件解压到独立的文件夹内需要一个解压缩过程,或者是人工解压,或者是由软件解压。解压后的运行效率会高一些。而直接使用.zip包的话,只需要让用户把插件复制到特定的位置即可,但是每次运行的时候都需要在内存里面解压缩,效率降低。另外,从.zip文件读取数据总是比较麻烦。推荐不包含没有数据文件的时候使用。
阅读全文
一个是AUTOCAD自带的芹雀伍vlisp程序,还有就是VBA,是傻瓜式的软件,这个也可以,方法跟VLISP的启动方法差不多,当然也可以用其他的编程软件,但是要查询的资料就更多了。 *** 作如嫌或下:
1、在cad工具栏下输入ap(appload),然后按下空格键,就会跳出加载/卸载应用程序,如下图
2、选择你要用的插件,然后点击加载
3、然后关闭就完成了.左下角显示已加载成功,表示你已经加载好了
4、如果要打开软件就已经加载插件了,那么就启动组上,点击内容
5、然后点击添岁圆加,找到你放插件的地方。添加完,然后关闭。重新打开CAD即可出现。
Lua 程序设计初步作者: 沐枫 (第二人生成员)
版权所有转载请注明原出处
在这篇文章中,我想向大家介绍如何进行Lua程序设计。我假设大家都学过至少一门编程语言,比如Basic或C,特别是C。因为Lua的最大用途是在宿主程序中作为脚本使用的。
Lua 的语法比较简单,学习起来也比较省力,但功能却并不弱。
在Lua中,一切都是变量,除了关键字。请记住这句话。
I. 首先是注释
写一个程序,总是少不了注释的。
在Lua中,你可以使用单行注释和多行注释。
单行注释中,连续两个减号"--"表示注释的开始,一直延续到行末为止。相当于C++语言中的"//"。
多行注释中,由"--[["表示注释开始,并且一直延续到"]]"为止。这种注释相当于C语言中的"/*…*/"。在注释当中,"[["和"]]"是可以嵌套的。
II. Lua编程
经典的"Hello world"的程序总是被用来开始介绍一种语言。在Lua中,写一个这样的程序很简单:
print("Hello world")
在Lua中,语句之间可以用分号";"隔开,也可以用空白隔开。一般来说,如果多个语句写在同一行的话,建议总是用分号隔开。
Lua 有好几种程序控制语句,如:
条件控制:if 条件 then … elseif 条件 then … else … end
While循环:while 条件 do … end
Repeat循环:repeat … until 条件
For循环:for 变量 = 初值,终点值,步进 do … end
For循环:for 变量1,变量2,… ,变量N in表或枚举函数 do … end
注意一下,for的循环变量总是只袭粗源作用于for的局部变量,你也可以省略步进值,这时候,for循环会使用1作为步进值。
你可以用break来中止一个循环。
如果你有程序设计的拍态基础,比如你学过Basic,C之类的,你会觉得Lua也不难。但Lua有几个地方是明显不同于这些程序设计语言的,所以请特别注意。
.语句块
语句块在C++中是用"{"和"}"凳高括起来的,在Lua中,它是用do 和 end 括起来的。比如:
do print("Hello") end
你可以在 函数 中和 语句块 中定局部变量。
.赋值语句
赋值语句在Lua被强化了。它可以同时给多个变量赋值。
例如:
a,b,c,d=1,2,3,4
甚至是:
a,b=b,a -- 多么方便的交换变量功能啊。
在默认情况下,变量总是认为是全局的。假如你要定义局部变量,则在第一次赋值的时候,需要用local说明。比如:
local a,b,c = 1,2,3 -- a,b,c都是局部变量
.数值运算
和C语言一样,支持 +, -, *, /。但Lua还多了一个"^"。这表示指数乘方运算。比如2^3 结果为8, 2^4结果为16。
连接两个字符串,可以用".."运处符。如:
"This a " .. "string." -- 等于 "this a string"
.比较运算
<><= >= == ~=
分别表示 小于,大于,不大于,不小于,相等,不相等
所有这些 *** 作符总是返回true或false。
对于Table,Function和Userdata类型的数据,只有 == 和 ~=可以用。相等表示两个变量引用的是同一个数据。比如:
a={1,2}
b=a
print(a==b, a~=b) -- true, false
a={1,2}
b={1,2}
print(a==b, a~=b) -- false, true
.逻辑运算
and, or, not
其中,and 和 or 与C语言区别特别大。
在这里,请先记住,在Lua中,只有false和nil才计算为false,其它任何数据都计算为true,0也是true!
and 和 or的运算结果不是true和false,而是和它的两个 *** 作数相关。
a and b:如果a为false,则返回a;否则返回b
a or b:如果 a 为true,则返回a;否则返回b
举几个例子:
print(4 and 5) -->5
print(nil and 13) -->nil
print(false and 13) -->false
print(4 or 5) -->4
print(false or 5) -->5
在Lua中这是很有用的特性,也是比较令人混洧的特性。
我们可以模拟C语言中的语句:x = a? b : c,在Lua中,可以写成:x = a and b or c。
最有用的语句是: x = x or v,它相当于:if not x then x = v end 。
.运算符优先级,从高到低顺序如下:
^
not - (一元运算)
* /
+ -
..(字符串连接)
<><= >= ~= ==
and
or
III. 关键字
关键字是不能做为变量的。Lua的关键字不多,就以下几个:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until while
IV. 变量类型
怎么确定一个变量是什么类型的呢?大家可以用type()函数来检查。Lua支持的类型有以下几种:
Nil 空值,所有没有使用过的变量,都是nil。nil既是值,又是类型。
Boolean 布尔值
Number 数值,在Lua里,数值相当于C语言的double
String 字符串,如果你愿意的话,字符串是可以包含''字符的
Table 关系表类型,这个类型功能比较强大,我们在后面慢慢说。
Function 函数类型,不要怀疑,函数也是一种类型,也就是说,所有的函数,它本身就是一个变量。
Userdata 嗯,这个类型专门用来和Lua的宿主打交道的。宿主通常是用C和C++来编写的,在这种情况下,Userdata可以是宿主的任意数据类型,常用的有Struct和指针。
Thread 线程类型,在Lua中没有真正的线程。Lua中可以将一个函数分成几部份运行。如果感兴趣的话,可以去看看Lua的文档。
V. 变量的定义
所有的语言,都要用到变量。在Lua中,不管你在什么地方使用变量,都不需要声明,并且所有的这些变量总是全局变量,除非,你在前面加上"local"。
这一点要特别注意,因为你可能想在函数里使用局部变量,却忘了用local来说明。
至于变量名字,它是大小写相关的。也就是说,A和a是两个不同的变量。
定义一个变量的方法就是赋值。"=" *** 作就是用来赋值的
我们一起来定义几种常用类型的变量吧。
A. Nil
正如前面所说的,没有使用过的变量的值,都是Nil。有时候我们也需要将一个变量清除,这时候,我们可以直接给变量赋以nil值。如:
var1=nil -- 请注意 nil 一定要小写
B. Boolean
布尔值通常是用在进行条件判断的时候。布尔值有两种:true 和 false。在Lua中,只有false和nil才被计算为false,而所有任何其它类型的值,都是true。比如0,空串等等,都是true。不要被C语言的习惯所误导,0在Lua中的的确确是true。你也可以直接给一个变量赋以Boolean类型的值,如:
varboolean = true
C. Number
在Lua中,是没有整数类型的,也不需要。一般情况下,只要数值不是很大(比如不超过100,000,000,000,000),是不会产生舍入误差的。在很多CPU上,实数的运算并不比整数慢。
实数的表示方法,同C语言类似,如:
4 0.4 4.57e-3 0.3e12 5e+20
D. String
字符串,总是一种非常常用的高级类型。在Lua中,你可以非常方便的定义很长很长的字符串。
字符串在Lua中有几种方法来表示,最通用的方法,是用双引号或单引号来括起一个字符串的,如:
"This is a string."
和C语言相同的,它支持一些转义字符,列表如下:
a bell
back space
f form feed
newline
carriage return
horizontal tab
v vertical tab
\ backslash
" double quote
' single quote
[ left square bracket
] right square bracket
由于这种字符串只能写在一行中,因此,不可避免的要用到转义字符。加入了转义字符的串,看起来实在是不敢恭维,比如:
"one line
next line
"in quotes", 'in quotes'"
一大堆的""符号让人看起来很倒胃口。如果你与我有同感,那么,我们在Lua中,可以用另一种表示方法:用"[["和"]]"将多行的字符串括起来,如:
page = [[
<HTML>
<HEAD>
<TITLE>An HTML Page</TITLE>
</HEAD>
<BODY>
<A HREF="http://www.lua.org
此网页不属于NGA网站,NGA不保证其安全性
继续访问 取消 不再提示我[ http://www.lua.org ]">Lua</A>
[[a text between double brackets]]
</BODY>
</HTML>
]]
值得注意的是,在这种字符串中,如果含有单独使用的"[["或"]]"就仍然得用"["或"]"来避免歧义。当然,这种情况是极少会发生的。
E. Table
关系表类型,这是一个很强大的类型。我们可以把这个类型看作是一个数组。只是C语言的数组,只能用正整数来作索引;在Lua中,你可以用任意类型来作数组的索引,除了nil。同样,在C语言中,数组的内容只允许一种类型;在Lua中,你也可以用任意类型的值来作数组的内容,除了nil。
Table的定义很简单,它的主要特征是用"{"和"}"来括起一系列数据元素的。比如:
T1 = {} -- 定义一个空表
T1[1]=10 -- 然后我们就可以象C语言一样来使用它了。
T1["John"]={Age=27, Gender="Male"}
这一句相当于:
T1["John"]={} -- 必须先定义成一个表,还记得未定义的变量是nil类型吗
T1["John"]["Age"]=27
T1["John"]["Gender"]="Male"
当表的索引是字符串的时候,我们可以简写成:
T1.John={}
T1.John.Age=27
T1.John.Gender="Male"
或
T1.John{Age=27, Gender="Male"}
这是一个很强的特性。
在定义表的时候,我们可以把所有的数据内容一起写在"{"和"}"之间,这样子是非常方便,而且很好看。比如,前面的T1的定义,我们可以这么写:
T1=
{
10, -- 相当于 [1] = 10
[100] = 40,
John= -- 如果你原意,你还可以写成:["John"] =
{
Age=27, -- 如果你原意,你还可以写成:["Age"] =27
Gender=Male -- 如果你原意,你还可以写成:["Gender"] =Male
},
20 -- 相当于 [2] = 20
}
看起来很漂亮,不是吗?我们在写的时候,需要注意三点:
第一,所有元素之间,总是用逗号","隔开;
第二,所有索引值都需要用"["和"]"括起来;如果是字符串,还可以去掉引号和中括号;
第三,如果不写索引,则索引就会被认为是数字,并按顺序自动从1往后编;
表类型的构造是如此的方便,以致于常常被人用来代替配置文件。是的,不用怀疑,它比ini文件要漂亮,并且强大的多。
F. Function
函数,在Lua中,函数的定义也很简单。典型的定义如下:
function add(a,b) -- add 是函数名字,a和b是参数名字
return a+b -- return 用来返回函数的运行结果
end
请注意,return语言一定要写在end之前。假如你非要在中间放上一句return,那么请写成:do return end。
还记得前面说过,函数也是变量类型吗?上面的函数定义,其实相当于:
add = function (a,b) return a+b end
当你重新给add赋值时,它就不再表示这个函数了。你甚至可以赋给add任意数据,包括nil (这样,你就清除了add变量)。Function是不是很象C语言的函数指针呢?
和C语言一样,Lua的函数可以接受可变参数个数,它同样是用"…"来定义的,比如:
function sum (a,b,…)
如果想取得…所代表的参数,可以在函数中访问arg局部变量(表类型)得到。
如 sum(1,2,3,4)
则,在函数中,a = 1, b = 2, arg = {3, 4}
更可贵的是,它可以同时返回多个结果,比如:
function s()
return 1,2,3,4
end
a,b,c,d = s() -- 此时,a = 1, b = 2, c = 3, d = 4
前面说过,表类型可以拥有任意类型的值,包括函数!因此,有一个很强大的特性是,拥有函数的表,哦,我想更恰当的应该说是对象吧。Lua可以使用面向对象编程了。不信?那我举例如下:
t =
{
Age = 27
add = function(self, n) self.Age = self.Age+n end
}
print(t.Age) -- 27
t.add(t, 10)
print(t.Age) -- 37
不过,t.add(t,10) 这一句实在是有点土对吧?没关系,在Lua中,你可以简写成:
t:add(10) -- 相当于 t.add(t,10)
G. Userdata 和 Thread
这两个类型的话题,超出了本文的内容,就不打算细说了。
VI. 结束语
就这么结束了吗?当然不是,接下来,需要用Lua解释器,来帮助你理解和实践了。这篇小文只是帮助你大体了解Lua的语法。如果你有编程基础,相信会很快对Lua上手了。
就象C语言一样,Lua提供了相当多的标准函数来增强语言的功能。使用这些标准函数,你可以很方便的 *** 作各种数据类型,并处理输入输出。有关这方面的信息,你可以参考《Programming in Lua 》一书,你可以在网络上直接观看电子版,网址为:http://www.lua.org/pil/index.html
此网页不属于NGA网站,NGA不保证其安全性
继续访问 取消 不再提示我[ http://www.lua.org/pil/index.html ]
当然,Lua的最强大的功能是能与宿主程序亲蜜无间的合作,因此,下一篇文章,我会告诉大家,如何在你的程序中使用Lua语言作为脚本,使你的程序和Lua脚本进行交互。
BBS.NGACN.COM
[姚姚]
[1 楼] 猪小小 2005-06-29 14:11
级别: Veteran
发帖: 721
威望: 13
财富: 13
使用流程
1. 函数的使用
以下程序演示了如何在Lua中使用函数, 及局部变量
例e02.lua
-- functions
function pythagorean(a, b)
local c2 = a^2 + b^2
return sqrt(c2)
end
print(pythagorean(3,4))
运行结果
5
程序说明
在Lua中函数的定义格式为:
function 函数名(参数)
...
end
与Pascal语言不同, end不需要与begin配对, 只需要在函数结束后打个end就可以了.
本例函数的作用是已知直角三角形直角边, 求斜边长度. 参数a,b分别表示直角边长,
在函数内定义了local形变量用于存储斜边的平方. 与C语言相同, 定义在函数内的代
码不会被直接执行, 只有主程序调用时才会被执行.
local表示定义一个局部变量, 如果不加local刚表示c2为一个全局变量, local的作用域
是在最里层的end和其配对的关键字之间, 如if ... end, while ... end等。全局变量的
作用域是整个程序。
2. 循环语句
例e03.lua
-- Loops
for i=1,5 do
print("i is now " .. i)
end
运行结果
i is now 1
i is now 2
i is now 3
i is now 4
i is now 5
程序说明
这里偶们用到了for语句
for 变量 = 参数1, 参数2, 参数3 do
循环体
end
变量将以参数3为步长, 由参数1变化到参数2
例如:
for i=1,f(x) do print(i) end
for i=10,1,-1 do print(i) end
这里print("i is now " .. i)中,偶们用到了..,这是用来连接两个字符串的,
偶在(1)的试试看中提到的,不知道你们答对了没有。
虽然这里i是一个整型量,Lua在处理的时候会自动转成字符串型,不需偶们费心。
3. 条件分支语句
例e04.lua
-- Loops and conditionals
for i=1,5 do
print(“i is now “ .. i)
if i <2 then
print(“small”)
elseif i <4 then
print(“medium”)
else
print(“big”)
end
end
运行结果
i is now 1
small
i is now 2
medium
i is now 3
medium
i is now 4
big
i is now 5
big
程序说明
if else用法比较简单, 类似于C语言, 不过此处需要注意的是整个if只需要一个end,
哪怕用了多个elseif, 也是一个end.
例如
if op == "+" then
r = a + b
elseif op == "-" then
r = a - b
elseif op == "*" then
r = a*b
elseif op == "/" then
r = a/b
else
error("invalid operation")
end
4.试试看
Lua中除了for循环以外, 还支持多种循环, 请用while...do和repeat...until改写本文中的for程序
BBS.NGACN.COM
[姚姚]
[2 楼] 猪小小 2005-06-29 14:20
级别: Veteran
发帖: 721
威望: 13
财富: 13
数组的使用
1.简介
Lua语言只有一种基本数据结构, 那就是table, 所有其他数据结构如数组啦,
类啦, 都可以由table实现.
2.table的下标
例e05.lua
-- Arrays
myData = {}
myData[0] = “foo”
myData[1] = 42
-- Hash tables
myData[“bar”] = “baz”
-- Iterate through the
-- structure
for key, value in myData do
print(key .. “=“ .. value)
end
输出结果
0=foo
1=42
bar=baz
程序说明
首先定义了一个table myData={}, 然后用数字作为下标赋了两个值给它. 这种
定义方法类似于C中的数组, 但与数组不同的是, 每个数组元素不需要为相同类型,
就像本例中一个为整型, 一个为字符串.
程序第二部分, 以字符串做为下标, 又向table内增加了一个元素. 这种table非常
像STL里面的map. table下标可以为Lua所支持的任意基本类型, 除了nil值以外.
Lua对Table占用内存的处理是自动的, 如下面这段代码
a = {}
a["x"] = 10
b = a -- `b' refers to the same table as `a'
print(b["x"]) -->10
b["x"] = 20
print(a["x"]) -->20
a = nil -- now only `b' still refers to the table
b = nil -- now there are no references left to the table
b和a都指向相同的table, 只占用一块内存, 当执行到a = nil时, b仍然指向table,
而当执行到b=nil时, 因为没有指向table的变量了, 所以Lua会自动释放table所占内存
3.Table的嵌套
Table的使用还可以嵌套,如下例
例e06.lua
-- Table ‘constructor’
myPolygon = {
color=“blue”,
thickness=2,
npoints=4
{x=0, y=0},
{x=-10, y=0},
{x=-5, y=4},
{x=0, y=4}
}
-- Print the color
print(myPolygon[“color”])
-- Print it again using dot
-- notation
print(myPolygon.color)
-- The points are accessible
-- in myPolygon[1] to myPolygon[4]
-- Print the second point’s x
-- coordinate
print(myPolygon[2].x)
程序说明
首先建立一个table, 与上一例不同的是,在table的constructor里面有{x=0,y=0},
这是什么意思呢? 这其实就是一个小table, 定义在了大table之内, 小table的
table名省略了.
最后一行myPolygon[2].x,就是大table里面小table的访问方式.
BBS.NGACN.COM
[姚姚]
[3 楼] yhcs 2005-06-29 14:25
级别: 警告等级2
发帖: 89
威望: -9
这个要顶,好文收藏!
[4 楼] 猪小小 2005-06-29 14:38
级别: Veteran
发帖: 721
威望: 13
财富: 13
其他还有一些高级编写资料,但在魔兽限制255字的情况下估计也用不到。所以这里不贴了。
BBS.NGACN.COM
[姚姚]
[5 楼] camus 2005-06-29 21:52
级别: Civilian
发帖: 171
威望: 1
财富: 1
变量的问题:
(1)lua似乎是不需要声明变量的,想用哪个变量那么用的时候直接赋值就可以了。根据赋值的类型那么变量就被自动定了类型了。 这样说对么? (当然,后面文章说到了这个例子:虽然这里i是一个整型量,Lua在处理的时候会自动转成字符串型,不需偶们费心。 -_-)
(2)
文章里这样说:假如你要定义局部变量,则在第一次赋值的时候,需要用local说明。
那么我在写宏的时候经常直接赋值比如 mp=UnitMana("player")/UnitManaMax("player") ,那么这个mp就成了全局变量咯?
+++++++++++++++++++++++
逻辑运算的问题
(2)一般写宏的时候,and,or都有比较运算,所以还能理解,这里直接两个数值进行逻辑运算有点懵了,
没绕明白,比如说第一句就不太清楚,有false和nil的都清楚,因为给出了规则。楼主能解释一下么?
(3)
a and b:如果a为false,则返回a;否则返回b
a or b:如果 a 为true,则返回a;否则返回b
这个规则怎么解释,还是说lua就是这样规定的没什么解释的
++++++++++++++++++++
结构的问题
(4)文章中说到了if...then....elseif....
可是现在写宏的时候很多人是if....then....if....then....if...这样嵌套的,也符合标准吧?
++++++++++++++++++
最后,谢谢楼主提供好文,,,,
BBS.NGACN.COM
i miss you so much,that's true,,,,
[6 楼] 猪小小 2005-06-30 01:39
级别: Veteran
发帖: 721
威望: 13
财富: 13
1、没错,所有未经声明的变量都是全局的。。。。甚至包括你在function里定义的变量。我已经实验过,比如在一个宏里定义了一个变量a并赋值1,运行后,如果在另一个宏里也使用了变量a,宏里要是不重新赋值就调用的话,a的值就会还是你之前所使用的那个宏里赋的值1。
但一般不用担心冲突,因为大多情况下,大多人在一个宏内使用一个新变量都会习惯重新赋值,一般不会引起太大的问题。但如果你是真正需要一些全局变量并能方便的识别他,可以这样,在宏中把作为局部变量的都用小写字母,要用做全局变量的用大写字母,因为Lua是严格区分大小写的,所以这样你就可以方便使用局部变量和全局变量了。
2、3、这个的确有点让人糊涂。。。我来这样解释把,其实你说的比较运算就是逻辑运算,都是一样。只是Lua扩大了and和or 的使用范围。
其实通常and和or所能使用的范围仅仅是布尔型的数值,所能得到的结果也是只有布尔型。即and和or只能对false和true进行运算,其结果也同样不是false就是true。运算情况如下:
false and true = false true and false= false
false and false = false true and true = true
false or true = true true or false = true
false or false = false true or true = true
但Lua扩大了and和or 所能运算的数据类型,为了使其也能运算其他类型的数据。所以才做出了这个方便的定义(当然,你说糊涂的定义也行)
a and b:如果a为false,则返回a;否则返回b
a or b:如果 a 为true,则返回a;否则返回b
现在我们可以用false和true替换a和b运算试试:
a=false b=true
false and true = false
false or true = true
a=true b=false
true and false = false
true or false = true
a=false b=false
false and false =false
false or false = false
a=true b=true
true and true = true
true or true =true
看到了把,其实结果和我们之前正常的逻辑运算是一样的。Lua仅仅是扩大的and和or所能使用的范围而已。因为Lua本身对数据类型的定义没有苛刻的要求,所以她的运算也要能适应各种类型的数据才方便。(其实这也算不上什么解释,她就是这样的定义的,方便还是糊涂就是一念之间,哈哈)
3、当然符合标准啊,嵌套归嵌套的用法,elseif归elseif。这两种用法是不同的啊,虽然结果可能一样。但有些情况嵌套方便就用嵌套,elseif方便就用elseif罗。
BBS.NGACN.COM
[姚姚]
[7 楼] 夏日晴天 2005-06-30 07:12
级别: 警告等级1
发帖: 481
威望: -3
疯掉了。。。。。。。。。。。。。。。。。。。难道只有电脑高手才能玩这个游戏啊
BBS.NGACN.COM
三大愿望:[七区不卡][跨服战场不排队][9城开TBC]
[8 楼] szsharo 2005-06-30 09:47
级别: Civilian
发帖: 44
威望: 0
下面是引用夏日晴天于2005-06-30 07:12发表的:
疯掉了。。。。。。。。。。。。。。。。。。。难道只有电脑高手才能玩这个游戏啊
wow的精髓就是保证各个不同的人群都能找到自己的乐趣,
电脑高手未必有泡菜的键盘熟练,泡菜也未必知道插件和宏~ 并不妨碍各自的乐趣~
只要表干扰别人就好~
暴雪是一帮天才的组合,偶崇拜了10年了:P
楼主是高手,难得还这么有耐心,PFPF~
本想把自己的整合插件效果图发上来的,实在是懒得去找图片空间,以后吧~
我用的是大脚+flexbar+titan+Gather+supermacro+LAX+moveanything+...
昨天大脚更新了,偶点开了新功能,被告知interface使用超过20M内存,强制退出游戏~
@@ 至今大汗不已~~~
另,使用SuperMacro这个插件可以允许将一个宏脚本的最长字节增加到1023,
允许将快捷键直接绑定到宏上(而不是拉到动作条后再绑定),允许使用其它宏的名称来
调用其它的宏。安装后即可使用。 可以解决小小MM说的字节限制问题。
[ 此贴被szsharo在2005-07-24 10:00重新编辑 ]
BBS.NGACN.COM
术士素优雅高贵的职业,尤其素帅气的侏儒术士~:P
[9 楼] 最接近神的牛 2005-06-30 10:35
级别: Civilian
发帖: 5
威望: 1
财富: 1
高人
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)