lua学习笔记(二)

本文探讨了Lua中的函数特性,包括第一类值的概念、词法作用域(静态作用域)与动态作用域的对比,以及重点讲解了闭包的原理。通过示例展示了Lua函数可以存储在变量和表中,作为参数和返回值,以及如何在嵌套函数中访问外部变量。同时,提到了Lua的尾调用优化。
摘要由CSDN通过智能技术生成

一、深入lua函数

  1. 一些必要的概念:

    参考自:
    https://www.douban.com/note/183992679/?type=rec#sep
    http://en.wikipedia.org/wiki/First-class_object
    http://en.wikipedia.org/wiki/Scope
    
    • 第一类值:

    In programming language design, a first-class citizen (also type,object, entity, or value) in a given programming language is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, and assigned to a variable.

    第一类值具备的特征:
    可以存放在变量和数据结构中
    可以当做参数传递给函数
    可以作为函数的返回值
    可以在运行期间被创建
    独立于给定的名字
    
    • 词法界定(静态域):

    With lexical scope, a name always refers to its (more or less) local lexical environment. This is a property of the program text and is madeindependent of the runtime call stack by the language implementation. Because this matching only requires analysis of the static program text, this type of scoping is also called static scoping.

    • 静态域和动态域的比较:

    The use of local variables — of variable names with limited scope,that only exist within a specific function — helps avoid the risk of a name collision between two identically named variables. However,there are two very different approaches to answering this question:What does it mean to be “within” a function?

    In lexical scoping (or lexical scope; also called static scoping or static scope), if a variable name’s scope is a certain function, then its scope is the program text of the function definition: within that text, the variable name exists, and is bound to the variable’s value,but outside that text, the variable name does not exist. By contrast,in dynamic scoping (or dynamic scope), if a variable name’s scope is a certain function, then its scope is the time-period during which the function is executing: while the function is running, the variable name exists, and is bound to its variable, but after the function returns, the variable name does not exist. This means that if function f invokes a separately defined function g, then under lexical scoping, function g does not have access to f’s local variables (assuming the text of g is not inside the text of f), while under dynamic scoping, function g does have access to f’s local variables (since g is invoked during the invocation of f).

    x=1
    function g () { echo $x ; x=2 ; }
    function f () { local x=3 ; g ; }
    f # does this print 1, or 3?
    echo $x # does this print 1, or 2?
    

    Consider, for example, the program at right. The first line, creates a global variable x and initializes it to 1. The second line, defines a function g that prints out (“echoes”) the current value of x, and then sets x to 2 (overwriting the previous value). The third line, defines a function f that creates a local variable x (hiding the identically named global variable) and initializes it to 3, and then calls g. The fourth line, f, calls f. The fifth line, echo $x, prints out the current value of x.

    So, what exactly does this program print? It depends on the scoping rules. If the language of this program is one that uses lexical scoping, then g prints and modifies the global variable x (because g is defined outside f), so the program prints 1 and then 2. By contrast, if this language uses dynamic scoping, then g prints and modifies f’s local variable x (because g is called from within f), so the program prints 3 and then 1. (As it happens, the language of the program is Bash, which uses dynamic scoping; so the program prints 3 and then 1.)

    • 闭包:

    In programming languages, closures (also lexical closures or function closures) are a technique for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment:[1] a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or storage location to which the name was bound when the closure was created.[b] A closure—unlike a plain function—allows the function to access those captured variables through the closure’s reference to them, even when the function is invoked outside their scope.

    Example. The following program fragment defines a higher-order function startAt with a parameter x and a nested function incrementBy. The nested function incrementBy has access to x, because incrementBy is in the lexical scope of x, even though x is not local to incrementBy. The function startAt returns a closure containing the function incrementBy, which adds the y value to the x value, and a reference to the variable x from this invocation of startAt, so incrementBy will know where to find it once invoked:

    function startAt(x)
       function incrementBy(y)
           return x + y
       return incrementBy
    
    variable closure1 = startAt(1)
    variable closure2 = startAt(5)
    

    Note that, as startAt returns a function, the variables closure1 and closure2 are of function type. Invoking closure1(3) will return 4, while invoking closure2(3) will return 8. While closure1 and closure2 refer to the same function incrementBy, the associated environments differ, and invoking the closures will bind the name x to two distinct variables with different values in the two invocations, thus evaluating the function to different results.

2 . lua中的函数

lua中的函数是带有此法定界的第一类值。
lua函数可以存放在变量中,可以存放在表中,可以作为函数的参数,可以作为函数的返回值。lua中嵌套的函数可以范文他外部函数中的变量。

  • 对lua函数特征的举例说明:

    lua函数可以存放在变量和表中:
    a = {p = print}
    a.p("hello lua")
    print = math.sin
    a.p(print(1))
    sin = a.p
    sin(10,20)
    
    lua函数可以是没有名字的,函数名实际是一个只想函数的变量:
    function  foo(x)    return  x*2    end
    foo = function(x)   return  x*2    end    --[[函数定义实际是一个赋值语句--]]
    
    
    lua函数作为函数的参数:
    network = {
    {name = "grauna",    ip = "210.26.30.34"},
    {name = "arraial",   ip = "210.26.30.23"},
    {name = "lua",       ip = "210.26.30.12"}
    }
    table.sort(network,  
               function  (a,b)
               return   (a.name  >  b.name)
               end
     )
    对表中数据按照升序或者降序排列,类似函数对象
    
    lua中的闭包举例:
    function  newCounter()
        local  i = 0
        return  function()
            i = i + 1
            return  i
        end
    end
    
    c1 = newCounter()
    print(c1())         --[[1--]]
    print(c1())         --[[2--]]
    c2 = newCounter()
    print(c2())         --[[1--]]
    print(c1())         --[[3--]]
    print(c2())         --[[2--]]
    
        在函数newCounter的内部匿名函数中可以访问newCOunter中的局部变量i,在匿名函数内部,i不是全局变量也不是局部变量,
    称作外部的局部变量或者upvalue。简单来说,闭包是一个函数以及它的upvalues。
        上面这个例子中c1,c2是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包。闭包指值而不是函数
    
  • lua函数作为table的域的几种形式

    函数对表中的域赋值
    op = {}
    op.add = function(x,y) return x+y end
    op.sub = function(x,y) return x-y end
    
    使用表构造函数
    op = {
        add = function(x,y) return x+y end
        sub = function(x,y) return x-y end
    }
    
    lua提供的另一种语法方式
    op = {}
    function op.add(x,y)
        return x+y
    end
    function op.sub(x,y)
        return x-y
    end
    
  • lua函数作为局部函数

    函数保存在局部变量中通过使用该局部变量使用局部函数
    local f = function()
        ...
    end
    local g = function()
        f()
    end
    
    使用local关键字修饰函数为局部函数
    local function f()
        ...
    end
    
    声明递归局部函数的方式
    local fact--[[lua函数在编译时遇到fact(n-1)会去查找是否有这样的全局函数,此处提前声明该函数为局部函数,
    避免编译时不必要的错误--]]
    fact = function(n)
                    if n == 0 then
                        return 1
                    else
                        return n*fact(n-1)
                    end
    end
    
    lua中无论是递归局部函数或者非直接递归局部函数,都要先声明后定义(个人理解是作为一种局部性的东西,必须先有名字,
    以便编译时不会因为找不到名字而错误)。
    local f,g
    function g()
        f()
    end
    function f()
        g()
    end
    
  • lua中的尾调用(proper tail calls)又称作尾递归(proper tail recursion)

        一种形象的解释尾调用类似于函数结尾的goto语句(goto语句打破了正常的函数调用规则,打破了函数栈桢的正常跳转)
    
    尾调用举例:
        function f(x)
            return g(x)
        end
        f调用g之后不会进行任何操作,此处g的调用被称作尾调用。尾调用的函数结束时不会返回到调用者,因此尾调用之后
    程序不需要使用额外的栈保留调用者的信息。
    
    尾调用的特性:
    尾调用不需要使用栈空间,因此尾调用的层次可以无限制,则某些情况下不会导致栈溢出。
    function foo(n)
        if n > 0 then return foo(n-1) end
    end
    函数的调用不会导致栈溢出
    
    识别什么样的情况是尾调用:
    以下几种情况不是尾调用
    function f(x)
        g(x)
        return
    end
    return g(x)+1
    return x or g(x)
    return (g(x))
    
    在lua中形如return g(...)这种形式的调用才称之为尾调用
    可以将尾调用理解为以一种goto,在状态机的变成领域尾调用是非常有用的,状态机要求函数记住每一个状态,改变状态
    只需要goto(or call)一个特定的函数。尾调用(不需要返回调用函数的栈桢)只是一个goto到另外一个函数并不是传统的函数调用。
    

3.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值