lua 3

函数

Lua为面向对象式的调用也提供了一种特殊的语法——冒号操作符。表达式o.foo(o,x)的另一种写法是o:foo(x),

冒号操作符使调用o.foo时将o隐含地作为函数的第一歌参数。

一个Lua程序既可以使用以Lua编写的函数,又可以调用以C语言编写的函数。

function incCount(n)

n=n or 1

count = count+n

end

多重返回值

Lua具有一项非常与众不同的特征,允许函数返回多个结果。Lua的几个预定义函数就是返回多个值的。

Lua的几个预定义函数就是返回多个值的。例如,用于在字符串中定位一个模式(pattern)的函数string.find。

该函数若在字符串中找到了指定的模式,将返回匹配的起始字符和结尾字符的索引。在此就需要使用多重赋值

语句来接收函数的返回值。

s,e=string.find("hello Lua users", "Lua")

print(s,e)-->7 9

以Lua编写的函数同样可以返回多个结果,只需要在return关键字后列出所有的返回值即可。例如,需要写一个

函数用于查找数组中的最大元素,并返回该元素的位置:

function maximum(a)

local mi=1

local m=a[mi]

for i,val in ipairs(a) do

if val > m then

mi=i;m=val

end

end

return m,mi

end

print(maximum({8,10,23,12,5}))

Lua会调整一个函数的返回值数量以适应不同的调用情况。

变长参数

Lua中的函数还可以接受不同数量的实参。例如,在调用print时可以传入一个、二个或多个实参。虽然print

是用C语言编写的,但也可以用Lua编写这种能接受不同数量实参的函数。

下面是一个简单例子,这个函数返回所有参数的总和:

function add(...)

local s=0

for i,v in ipairs{...} do

s=s+v

end

return s

end

print(add(3,4,10,25,12))-->54

参数表中的3个点(...)表示该函数可以接受不同数量的实参。当这个函数被调用时,它的所有参数都会被

收集到一起。这部分收集起来的实参称为这个函数的“变长参数”。一个函数要访问它的变长参数时,仍需

用到3个点(...)。但不同的是,此时这3个点是作为一个表达式来使用的。在上例中,表达式{...}表示一

个由所有变长参数构成的数组。而函数add遍历了该数组,并累加了每个元素。

  表达式“...”的行为类似于一个具有多重返回值的函数,它返回的是当前函数的所有变长参数。例如:

  local a,b=...用第一个和第二个变长参数来初始化这两个局部变量。实际上,还可以通过变长参数

来模拟Lua中普通的参数传递机制,例如:

  function foo(a,b,c)

可以转换为:

  function foo(...)

  local a,b,c=...

  end

对于哪些喜爱Perl参数传递机制的人来说,可能会更倾向于第二种形式。

若有这样一个函数:

  function id(...)  return ... end

它只是简单地返回调用它时传入的所有实参,这是一个“多值恒定式”函数。下这个函数的行为非常类似于

直接调用函数foo,但在调用foo前先调用print打印出其所有的实参:

  function foo1(...)

  print("calling foo:",...)

   return foo(...)

  end

  这种技巧对于跟踪某个特定的函数调用很有帮助。

  Lua提供了专门用于格式化文本(string.format)和输出文本(io.write)的函数。很自然地会想到将函数

合二为一:

  function fwrite(fmt,...)

  return io.write(string.format(fmt,...))

  end

注意在3歌点前有一个固定参数fmt。具有变长参数的函数同样也可以拥有任意数量的固定参数,但固定参数必须放在变长参数之前。Lua会将前面的实参赋予固定参数,而将余下的实参视为变长参数。

  通常一个函数在遍历其变长参数时只需要使用表达式{...},这就像访问一个table一样,访问所有变长参数。然而在

某些特殊的情况下,变长参数中可能包含一些故意传入的nil,那么此时就需要用函数select来访问变长参数了。调用select时,必须传入一个固定实参selector(选择开关)和一些列变长参数。如果selector位数字n,那么select返回它的第n个可变实参;否则,selector可能为字符串“#”,这样select会返回变长参数的总数。下面的循环演示了如何使用select来遍历一个函数的所有变长参数:

  for i=1,select('#',...) do

  local arg=select(i,...)

  end

  select("#",...)会返回所有变长参数的总数,其中包括nil。

具名实参

  Lua中的参数传递机制是具有“位置性”的,也就是说在调用一个函数时,实参是通过它在参数表中的位置与形参匹配

起来的。第一个实参的值与第一个形参相匹配,依次类推。但有时通过名称来指定实参也是很有用的。

  os.rename,这个函数用于文件改名。通常会忘记第一个参数是表示新文件名还是旧文件名。因此,会希望这个函数能够接受两个具有名称的实参。

  rename{old="temp.lua",new="temp1.lua"}

  另一方面,将rename改为只接受一个参数,并从这个参数中获取实际的参数:

  function rename(arg)

  return os.rename(arg.old, arg.new)

  end

  若一个函数拥有大量的参数,而其中大部分参数是可选的话,这种参数传递风格会特别有用。例如在一个GUI库中,一个用于创建新窗口的函数可能会具有许多的参数,而其中大部分是可选的,那么最好使用具名实参:

  w=Window{x=0,y=0,width=300,height=200,

        title="Lua",background="blue",

        border=true

        }

  Window函数可以根据要求检查一些必填参数,或者为某些参数添加默认值。假设“_Window”才是真正用于创建窗口的函数,它要求所有参数以正确的次序传入,那么Window函数可以这么写:

  function Window(options)

  if type(options.titile) ~= "string" then

   error("no title")

  elseif type(options.width) ~= "number" then

   error("no width")

elseif type(options.height) ~= "number" then

   error("no height")

  end

  --其他参数都是可选的

  _Window(options.title,

        options.x or 0

        options.y or 0

        options.width,options.height,

       options.background or "white",

       options.border)

  end