::^乔乔^::明镜台::原创空间::C#.NET2.0,C++技术BLOG
人最重要的是心境,一颗平静安稳的心才能更好的进步,保持自己的心态.成为梦想中的高手QQ群:8664695
posts - 17,comments - 32,trackbacks - 0

1、lua学习之入门(一)----环境搭建

2、lua学习之入门(二)----基础语法1

3、lua学习之入门(二)----基础语法2

4、lua学习之入门(三)----函数

     在LUA里函数是个十分重要的内容,因为我们实际开发的时候,用的最多的就是函数,用函数是封装各个实现,在Q群里曾经听一些大虾说过,写LUA必须要懂得闭包,常用系统函数,还有范型for写迭代器,最后就是编译和运行还有错误信息.其实这章,我就卡了一下壳,卡壳的地方在闭包,因为我没有完全理解闭包的作用,和闭包的好处,网上也没有明确的说明闭包的优劣,所以闭包的概念,我也只能以个人感觉去写,如果我说得不对,欢迎赐教.如果对闭包理解很深,欢迎来指教...你要偶拜你为师也行,达者为师啊,偶很好学的.

函数有两种用途:1.完成指定的任务,这种情况下函数作为调用语句使用;2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。

   其实我们接触最早的,就是函数,还记得我们的第一个LUA吗?print();这就是一个函数.可以理解****()都可以当做是函数,其实大部分编程语言的函数都是以这个方式一共调用的.认识了什么是函数,那么我们就自己编写一个函数吧

function maxFun(a , b)
   
if  a > b then
   
return  a;
   
else
     
return  b;
  end
end

-- 我们用一个输出语句把我们的函数结果显示出来 , 证明我们写的函数没有问题

print (maxFun( 4 , 3 ));
上面这个函数
, 其实很简单就是比较两个数的大小 . 最后运行的结果我们得到结果是4 , 这里使用的是函数的第1种任务 , 判断两个数的大小 , 在LUA里函数是可以返回多个值的 , 这个点和其他编程语言有很大的区别 , 那就很容易造成错觉 , 多个返回值 , 那我如何获得各个返回值呢 ? 看例子

function returnAnyValue()
        
return   1 , 2 , 3 , 4 ;
   end

    a
, b , c , =  returnAnyValue();
e
, f , =  returnAnyValue();
h
, i , j , k , =  returnAnyValue();

-- [[
print ( " a: " .. a .. "  b: " .. b .. "  c: " .. c .. "  d: " .. d .. "  e: " .. e .. "  f: " .. f .. "  g: " .. g .. "  h: " .. h .. "  i: " .. i .. "  j: " .. j .. "  k: " .. k .. "  l: " .. l .. "  ! " );
]]
-- 这样编译是出错的 , 提示告诉我们l为nil不能输出 .

-- 所以我们把程序修改为

print ( " a: " .. a .. "  b: " .. b .. "  c: " .. c .. "  d: " .. d .. "  e: " .. e .. "  f: " .. f .. "  g: " .. g .. "  h: " .. h .. "  i: " .. i .. "  j: " .. j .. "  k: " .. k .. "  ! " );

结果如下
:
   a
: 1  b : 2  c : 3  d : 4  e : 1  f : 2  g : 3  h : 1  i : 2  j : 3  k : 4


虽然我们不能输出L的值,但是我们根据错误提示可以知道l的值是nil的.

论述完返回值,那么我们要看参数数了,LUA支持可变参数的模式的使用的方法和C语言的差不多,不熟悉的可以先学C语言,毕竟我看到的LUA大多数都是嵌入C中的,所以C/C++至少你要会.

好了我们要进入我觉得比较难的点:闭包

在我接触的编程语言里,其实还没发现过闭包的概念,坦白的说一句,就是LUA我才接触闭包的.闭包从网上的资料来看,就是JAVASCRIPT里的匿名函数的使用(我接触新语言的时候,总喜欢用自己接触过的语言去进行对比,去学习.我觉得这种学习的方法还是比较有效果的,有兴趣的朋友也可以试试这个方法.),在我看了网上公开的闭包程序,最大的特点就是使用,外面的函数有自己的局部变量,内部的匿名函数使用了外部函数的局部变量.有点像面向对象思想里的意思,后来我在LUA的一个论坛看到一篇关于闭包的定义,如下:

当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。虽然这看起来很清楚,事实并非如此,词法定界加上第一类函数在编程语言里是一个功能强大的概念,很少语言提供这种支持。
技术上来讲,闭包指值而不是指函数,函数仅仅是闭包的一个原型声明

这个是我在网上找的一个关于闭包的例子,个人感觉水平有限.可能使用网上的例子更有说服力

下面看一个简单的例子,假定有一个学生姓名的列表和一个学生名和成绩对应的表;现在想根据学生的成绩从高到低对学生进行排序,可以这样做:

names 
=  { " Peter " ,   " Paul " ,   " Mary " }

grades 
=  {Mary  =   10 ,  Paul  =   7 ,  Peter  =   8 }

table
. sort (names ,  function (n1 ,  n2)

    
return  grades[n1]  >  grades[n2]     --  compare the grades

end)
假定创建一个函数将上面的功能包装起来

   function sortbygrade (names, grades)

       table.sort(names, function (n1, n2)

          return grades[n1] > grades[n2]    -- compare the grades

    end)

end
例子中包含在sortbygrade函数内部的sort中的匿名函数可以访问sortbygrade的参数grades,在匿名函数内部grades不是全局变量也不是局部变量,我们称作外部的局部变量(external local variable)或者upvalue。(upvalue意思有些误导,然而在Lua中他的存在有历史的根源,还有他比起external local variable简短)。


这个例子主要说明的是在外部函数和内部函数之见参数的使用,函数中就可以直接用变量进行传值,这里需要说明一下的是table.sort(存放元素的数组,排序函数)具体的等后面我介绍常用库的时候说.只要知道是表排序就好.

函数就写出来的,但是我们需要更直观的看到这些资料
所以我在最下面加上了如下代码

print ( " --------- " )
sortbygrade (names
,  grades);

for  v in pairs(names)  do

print (names[v])
end
输出结果为
Mary
Peter
Paul
我将grades = {Mary = 7, Paul = 8, Peter = 9}改了再看效果
Peter
Paul
Mary

function newCounter()

    
local i = 0

    
return function()     -- anonymous function

       i 
= i + 1

        
return i

    end

end

 

c1 
= newCounter()

print(c1())  --> 1

print(c1())  --> 2

print(c1())  --> 3

结果代码里已经显示了,我就不说了,但是大家看到这个结果?你想到了什么?
对象?就是对象,看以下C#代码
public class CC
{
    
private int a;

    
public CC()
    
{
       a
=0;
    }


    
public int newCounter()
    
{
       
this.a=this.a+1;
       retrun 
this.a;
    }

}

如果LUA代码里C1=newCounter();C2=newCounter()
 print(c2())--->1
是不是跟我们声明一个对象十分的类似呢?所以我推断闭包在一定程度上实现了部分面向对象的功能,肯定有一定的差别的,我只是说类似..而且我看代码有点像C语言去实现面向对象的感觉...

闭包还可以实现类似JAVA里沙箱的功能,(来自网上,我并没有运行这段代码)
do

    local oldOpen 
= io.open

    io.
open = function (filename, mode)

       
if access_OK(filename, mode) then

           
return oldOpen(filename, mode)

       
else

           
return nil, "access denied"

       
end

    
end

end

*正确的尾调用
这个迷宫游戏是典型的状态机,每个当前的房间是一个状态。我们可以对每个房间写一个函数实现这个迷宫游戏,我们使用尾调用从一个房间移动到另外一个房间。一个四个房间的迷宫代码如下:

function room1 ()

    local move 
= io.read()

    
if move == "south" then

       
return room3()

    elseif move 
== "east" then

       
return room2()

    
else

       
print("invalid move")

       
return room1()   -- stay in the same room

    
end

end

 

function room2 ()

    local move 
= io.read()

    
if move == "south" then

       
return room4()

    elseif move 
== "west" then

       
return room1()

    
else

       
print("invalid move")

       
return room2()

    
end

end

 

function room3 ()

    local move 
= io.read()

    
if move == "north" then

       
return room1()

    elseif move 
== "east" then

       
return room4()

    
else

       
print("invalid move")

       
return room3()

    
end

end

 

function room4 ()

    
print("congratilations!")

end

我们可以调用room1()开始这个游戏。

如果没有正确的尾调用,每次移动都要创建一个栈,多次移动后可能导致栈溢出。但正确的尾调用可以无限制的尾调用,因为每次尾调用只是一个goto到另外一个函数并不是传统的函数调用
(以上代码来源网上)
正确尾调用论述的意义在于..栈溢出的问题,不正确的尾调用是存在栈溢出问题的.

这篇写的我自己都不是很满意...因为这个东西..需要点时间消化...等我消化了再来整理一下,就好象开头,我也是一个LUA新手,我只能把我认识的弄出来,而且正确上..也非绝对..其实我的目的只是传播我的学习的思想.

明鏡臺
posted on 2009-05-14 13:14 ^乔乔^ 阅读(1919) 评论(0)  编辑 收藏 引用 所属分类: Lua学习笔记

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理