什么是环境?

http://www.lua.org/manual/5.1/manual.html#2.9

Besides metatables, objects of types thread, function, and userdata have another table associated with them, called their environment. Like metatables, environments are regular tables and multiple objects can share the same environment.

线程、函数和用户自定义数据, 一张表与之关联,即环境表, 环境表也是章普通表, 多个对象可以共享同一个环境表。

Threads are created sharing the environment of the creating thread. Userdata and C functions are created sharing the environment of the creating C function. Non-nested Lua functions (created by loadfile, loadstring or load) are created sharing the environment of the creating thread. Nested Lua functions are created sharing the environment of the creating Lua function.

被创建的线程共享创建线程的环境。

被创建的用户数据和C函数共享 创建者(C函数)的环境。

Non-nested Lua 函数(由loadfile和loastring和load)被创建后, 共享创建者(线程)的环境。

Nested Lua函数被创建,共享创建者(Lua函数)的环境。

Environments associated with threads are called global environments. They are used as the default environment for threads and non-nested Lua functions created by the thread and can be directly accessed by C code (see §3.3).

关联线程的环境, 被称作全局环境。 线程和Non-nested函数被线程创建后, 默认的环境就是全局环境, 这些环境可以被C代码直接访问。

The environment associated with a C function can be directly accessed by C code (see §3.3). It is used as the default environment for other C functions and userdata created by the function.

C函数关联的环境,可以直接被C代码访问。

C函数创建的C函数和用户数据, 则将这个环境被作为默认环境。

Environments associated with Lua functions are used to resolve all accesses to global variables within the function (see §2.3). They are used as the default environment for nested Lua functions created by the function.

lua函数关联的环境,被用作解析所有的全局函数的访问, 在此函数中。

这个环境被用作如下情况的默认环境, lua函数创建的了nested lua函数, 则nested lua函数,则集成了 lua函数的环境。

You can change the environment of a Lua function or the running thread by calling setfenv. You can get the environment of a Lua function or the running thread by calling getfenv. To manipulate the environment of other objects (userdata, C functions, other threads) you must use the C API.

使用setfenv改变lua函数或者线程的环境。

使用getfenv获取lua函数或者线程的环境。

操作其它对象(userdata c函数 其它线程)的环境, 需要使用C API.

LUA thread

The type thread represents independent threads of execution and it is used to implement coroutines (see §2.11). Do not confuse Lua threads with operating-system threads. Lua supports coroutines on all systems, even those that do not support threads.

线程表达了一个独立的执行线索,它用于实现协程。

不要把lua的线程的操作系统的线程混淆。

lua支持协程在所有的系统上, 即使在那些不支持线程的系统上。

协程

Lua supports coroutines, also called collaborative multithreading. A coroutine in Lua represents an independent thread of execution. Unlike threads in multithread systems, however, a coroutine only suspends its execution by explicitly calling a yield function.

http://www.lua.org/manual/5.1/manual.html#2.11

http://www.cnblogs.com/chenny7/p/3634529.html

协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈、局部变量、指令指针,但与其它协程共享全局变量等很多信息。

协程类似一种多线程,但与多线程还有很多区别:

1. 协程并非os线程,所以创建、切换开销比线程相对要小。
2. 协程与线程一样有自己的栈、局部变量等,但是协程的栈是在用户进程空间模拟的,所以创建、切换开销很小。
3. 多线程程序是多个线程并发执行,也就是说在一瞬间有多个控制流在执行。而协程强调的是一种多个协程间协作的关系,只有当一个协程主动放弃执行权,另一个协程才能获得执行权,所以在某一瞬间,多个协程间只有一个在运行。
4. 由于多个协程时只有一个在运行,所以对于临界区的访问不需要加锁,而多线程的情况则必须加锁。
5. 多线程程序由于有多个控制流,所以程序的行为不可控,而多个协程的执行是由开发者定义的所以是可控的。

Lua的协程是不对称的(asymmetric coroutines),是指“挂起一个正在执行的协同函数” 和 “使一个被挂起的协程再次执行的函数”是不同的。

有些语言使用对称协同(symmetric coroutines),即使用同一个函数负责“执行与挂起间的状态切换”。

Non-nested lua function

Non-nested Lua functions (created by loadfile, loadstring or load) are created sharing the environment of the creating thread.

Non-nested lua functions 没有具体例子说明,到底是什么样的函数, 只说是由 loadfile  loadstring 和 load产生的, 可以理解为脚本文件对应的一个匿名函数。

这些函数继承了 创建者线程的 环境。

Environments associated with threads are called global environments. They are used as the default environment for threads and non-nested Lua functions created by the thread and can be directly accessed by C code (see §3.3).

线程相关的环境被称为全局环境, 当线程创建了  线程或者 non-nested lua函数 时候,  被创建者的默认环境, 就继承了创建者线程的环境。

所以, Non-nested lua functions 也具有全局环境。

对于 require语句, 调用一个脚本情况, require实际上相当于 local  f=loadfile(filename); f()

整个脚本对应的匿名函数, 就具有创建者线程相同的环境, 创建者线程通过 require方法加载, 查询loader,找到lua脚本的加载器:

static int loader_Lua (lua_State *L) {
  const char *filename;
  const char *name = luaL_checkstring(L, 1);
  filename = findfile(L, name, "path");
  if (filename == NULL) return 1;  /* library not found in this path */
  if (luaL_loadfile(L, filename) != 0)
    loaderror(L, filename);
  return 1;  /* library loaded successfully */
}

require函数的环境为 _G

require函数的实现简写为,  则filename脚本被创造为 chunk对应的 匿名函数 f, f默认继承了 require的环境 _G

function require(filename)

if package.loaded[filename] then

return package.loaded[filename]

end

local f = assert(loadfile(filename))

return f()

end

当然如果 filename 脚本中有 module语句, 则会改写 匿名函数的环境。

可以通过本文的给出的脚本进行验证。

main.lua

local module_mediator = require("module_mediator")

var_caller = 55

local function printTable(tbl)

for k,v in pairs(tbl) do
        print("kay="..tostring(k) .. "  value="..tostring(v))
    end

end

print("----------- module_mediator ------------------")

printTable(module_mediator)

print("----------- _G ------------------")

printTable(_G)

module_mediator.lua

local require = require
local setmetatable = setmetatable
local setfenv = setfenv
local getfenv = getfenv
local tostring = tostring
local print = print

module("module_mediator", package.seeall)

var_mediator = 77

--[[

local require_env = {var_mediator=33}
setmetatable(require_env, {__index=_G})

setfenv(require, require_env)
]]

local require_env = getfenv(require)

local function printTable(tbl)

for k,v in pairs(tbl) do
        print("kay="..tostring(k) .. "  value="..tostring(v))
    end

end

print(" require_env ="..tostring(require_env))

print("----------- before require env ------------------")

printTable(require_env)

print("----------- after require env ------------------")

local module_test = require("module_test")

module_test.lua (将module 添加和去除, 本脚本中的 getfenv(1) 和 getfenv(2)结果进行比较):

去掉module, 打印:

getfenv(1) =table: 005D2658  // module_test.lua匿名函数 环境
getfenv(1) metatable=nil
getfenv(2) =table: 005D2658 // require 的环境, _G

kay=_G  value=table: 005D2658

添加module, 打印:

getfenv(1) =table: 00773C68 // module_test.lua匿名函数 环境

getfenv(1) metatable=nil
getfenv(2) =table: 00252658 // require 的环境, _G

kay=_G  value=table: 00252658

--module(..., package.seeall)

local print = print
local getfenv = getfenv
local tostring = tostring
local getmetatable = getmetatable
local pairs = pairs

module("module_test")

local function printTable(tbl)

for k,v in pairs(tbl) do
        print("kay="..tostring(k) .. "  value="..tostring(v))
    end

end

varone = 1

local strkey = "vartwo"

-- 本模块环境
local env = getfenv(1)

env[strkey] = 2

print("vartwo="..vartwo)

print("getfenv(1) ="..tostring( ( getfenv(1) ) ) )

print("getfenv(1) metatable="..tostring( getmetatable( getfenv(1) ) ) )

if getmetatable( getfenv(1) )  then
print("--------------- before getmetatable( getfenv(1) ) -------------")
printTable( getmetatable( getfenv(1) ) )
print("--------------- after getmetatable( getfenv(1) ) -------------")
end

-- 调用此模块的环境
local env_caller = getfenv(2)

env["env_caller"] = env_caller

print("getfenv(2) ="..tostring(getfenv(2)))

print("--------------- before env_caller -------------")
printTable( env_caller )
print("--------------- after env_caller -------------")

脚本文件与chunk与匿名函数

http://www.lua.org/manual/5.1/manual.html#2.11

The unit of execution of Lua is called a chunk. A chunk is simply a sequence of statements, which are executed sequentially. Each statement can be optionally followed by a semicolon:

	chunk ::= {stat [`;´]}

There are no empty statements and thus ';;' is not legal.

lua的执行单元是chunk, chunk是有一系列的语句组成。

Lua handles a chunk as the body of an anonymous function with a variable number of arguments (see §2.5.9). As such, chunks can define local variables, receive arguments, and return values.

lua将一个chunk作为一个匿名函数对待, 带有一系列的入参。

所以,chunk可以定义局部变量, 接受参数, 返回值。

A chunk can be stored in a file or in a string inside the host program. To execute a chunk, Lua first pre-compiles the chunk into instructions for a virtual machine, and then it executes the compiled code with an interpreter for the virtual machine.

chunk存在两种形式, 文件 或者 字符串。

执行chunk需要先将chunk编译为指令,这个是虚拟机指令, 然后执行编译后的代码, 使用虚拟机解释器。

Chunks can also be pre-compiled into binary form; see program luac for details. Programs in source and compiled forms are interchangeable; Lua automatically detects the file type and acts accordingly.

chunk可以被预先编译为二进制形式, 详情参与 luac。

源码和编译形式是可以互换的。lua会自动探测, 文件类型, 并执行响应动作。

如果是编译后的文件, 则lua不会再编译,直接执行, 如果未编译, lua会先编译后执行。

lua require dofile loadfile区别

http://blog.163.com/hbu_lijian/blog/static/126129153201422902256778/

1.dofile与loadfile

dofile当作Lua运行代码的chunk的一种原始的操作。dofile实际上是一个辅助的函数。真正完成功能的函数是loadfile;与dofile不同的是loadfile编译代码成中间码并且返回编译后的chunk作为一个函数,而不执行代码;另外loadfile不会抛出错误信息而是返回错误代。我们可以这样定义dofile:

function dofile (filename)

local f = assert(loadfile(filename))

return f()

end

3.require与dofile

。粗略的说require和dofile完成同样的功能但有两点不同:

1. require会搜索目录加载文件

2. require会判断是否文件已经加载避免重复加载同一文件。由于上述特征,require在Lua中是加载库的更好的函数。

Nested lua luafunction

Nested Lua functions are created sharing the environment of the creating Lua function.

嵌套lua函数共享了  创建者的环境。

什么是嵌套的lua函数呢? 没有官方定义。

http://lua-users.org/wiki/LuaScopingDiscussion

这个有个关于嵌套函数作用域的讨论,  函数curry化的用法, 被嵌套的是 sum函数:

function addn(x)
function sum(y)
return x+y
end
return sum
end
print((addn(3))(1))

通过样例解释嵌套函数环境

getfenvtest.lua

local getfenv = getfenv
local print = print

module("getfenvtest")

var_test = 1

function test()

local env = getfenv(1)
    print("var_test="..env["var_test"])

local env_caller = getfenv(2)
    print("env_caller[var_test]="..env_caller["var_test"])
end

test()

getfenv_main.lua

var_test = 100

local testmodule = require("getfenvtest")

local function printTable(tbl)

for k,v in pairs(tbl) do
        print("kay="..tostring(k) .. "  value="..tostring(v))
    end

end

print("-------- before test func env ------")
printTable(getfenv(testmodule.test))
print("-------- after test func env ------")

function mediator()

testmodule.test()

end

local env = {}
env.testmodule = testmodule
env.mediatorflag = true
env.var_test = 55

setfenv(mediator, env)

mediator()

打印:

var_test=1  // getfenv_test.lua中 调用test打印,  说明test函数继承了 此脚本的匿名函数环境
env_caller[var_test]=1   // getfenv_test.lua中 调用test打印,  此脚本的匿名函数调用了 此函数, 故此函数中getfenv(2) 返回的是 此脚本匿名函数的环境

-------- before test func env ------ // 打印标明 虽然在 被调用脚本中使用, 但是test函数的环境,还是其创建时候的环境, 即getenvtest.lua中 module环境。
kay=_NAME  value=getfenvtest
kay=_PACKAGE  value=
kay=test  value=function: 00539898
kay=_M  value=table: 00539168
kay=var_test  value=1
-------- after test func env ------
var_test=1 // 虽然在被调用脚本中使用, getfenv获得的环境还是 getfenvtest.lua中module的环境, 跟调用点没有关系。
env_caller[var_test]=55 // 在被调用脚本中使用, getfenv(2)获得的是调用脚本的环境。
>Exit code: 0

一个有趣的环境改变实验

话说一个聪明人, 能够根据环境讲不同的话语, 见到美国人, 说美国是个伟大的国家, 见到中国人, 中国是个伟大的国家, 背地里没有外国人的时候, 说MyCountry是个伟大的国家。

首先定义 聪明人为 wiseman.lua 使用module构造。

其中有一个关键函数是  makeEnvDependFunc, 将一个普通的函数,转换为, 判断会判断环境的函数。

实现原理为 改造此函数的依赖的 环境 setfenv, 当判断被调用的场景getfenv(2), 如果存在, 则重写环境为 {}

并将此{}的metatable设置为 __index=function()end, 函数体中会先寻找 getfenv(2), 然后在找getfenv(1)。

module("wiseman", package.seeall)

country = "My country"

local function makeEnvDependFunc(f)
    local oldfenv = getfenv(f)

-- 缂撳瓨鎵€鏈夌殑env
    local envTbl = {}

table.insert( envTbl, oldfenv )

-- environment, and isprior
    local addNewEnv = function( env, isprior )
        if isprior then
            table.insert(envTbl, 1, env)
        else
            table.insert(envTbl, env)
        end
    end

local deleteNewEnv = function(targetEnv)
        for i,env in ipairs(envTbl) do
            if targetEnv == env then
                table.remove(envTbl, i)
            end
        end
    end

local indexEnvTbl = function(env, key)
        for i,env in ipairs(envTbl) do
            local val = env[key]
            if val then
                return val
            end
        end

return nil
    end

local envMetaTable = {__index=indexEnvTbl}
    local newfenv = {}
    setmetatable(newfenv, envMetaTable)

local fwrapper = function()
        addNewEnv(getfenv(2), true)

setfenv(f, newfenv)

local ret = f()

deleteNewEnv(getfenv(2))

setfenv(f, oldfenv)

return ret
    end

return fwrapper
end

--[[
function SayHello()
    local env = getfenv(1)

local country_senario = country

local env_caller = getfenv(2)
    if env_caller and env_caller.country then
        country_senario = env_caller.country
    end

print(country_senario .." is a great country!")
end
]]

-- 姝ゆ椂 country 杩樻槸鎸囨湰module锛?My Country
function SayHello_Naive()
    print(country .." is a great country!")
end

SayHello = makeEnvDependFunc(SayHello_Naive)

china.lua

module("china")

country = "china"

function CallPerson( wiseman )
    wiseman.SayHello()
end

american.lua

module("american")

country = "american"

function CallPerson( wiseman )
    wiseman.SayHello()
end

主调脚本actionbycountry.lua

local wiseman = require("wiseman")

local chinese = require("china")
chinese.CallPerson(wiseman)

local american = require("american")
american.CallPerson(wiseman)

wiseman.SayHello()

打印:

>lua -e "io.stdout:setvbuf 'no'" "actionbycountry.lua"
china is a great country!
american is a great country!
My country is a great country!
>Exit code: 0

实验脚本如下:

https://github.com/fanqingsong/code-snippet/tree/master/lua/envtest

lua 环境揭秘的更多相关文章

  1. 【Lua学习笔记之:Lua环境搭建 Windows 不用 visual studio】

    Lua 环境搭建 Windows 不用 visual studio 系统环境:Win7 64bit 联系方式:yexiaopeng1992@126.com 前言: 最近需要学习Unity3d游戏中的热 ...

  2. lua 学习 (一 )Mac搭建lua环境和ide

    今天开始学习lua  首先是搭建环境 和 我的hello world 首先 交代一下我用的是 Mac 系统 所以在Mac上安装lua 环境很方便的 lua  Mointain Lion - Lua 5 ...

  3. VS2012+LUA环境搭建

    1 .启动VS2012,选择C++下的"win32"项目类型中的"Win2控制台应用程序" 2.工具——选项——项目和解决方案——VC++目录——可执行程序(C ...

  4. Nginx+lua环境搭建

    其实有点类似WampServer一站式安装包 wget http://openresty.org/download/ngx_openresty-1.7.10.1.tar.gz tar -zxvf ng ...

  5. VS2012配置Lua环境

    1.VS2012配置BabeLua插件 2.VS2012配置Lua 1.VS2012配置BabeLua插件 BabeLua插件简介: 安装方法: 关闭VS2012后直接安装BabeLua插件. 下载地 ...

  6. [Lua]Mac系统上安装Lua环境

    1.下载 Lua语言的官方网站 http://www.lua.org/ 下载最新版本的Lua环境 2.安装 解压下载包lua-5.3.1.tar.gz 打开终端Terminal 使用cd命令进入该目录 ...

  7. Mac 端配置 Lua 环境

    一.设计目的 Lua 是一种轻量级的脚本语言,其目的是为了嵌入到程序中,从而为程序提供灵活的扩展和定制功能. 二.特性 轻量级:编译后仅仅 100 余K,可以很方便的嵌入到程序中. 可扩展:Lua 提 ...

  8. 将C注册到lua环境中使用

    注册到lua的方式有两种,一种是lua解释器,如果支持动态链接,使用动态链接机制,将函数接口编译成动态链接库,然后将动态链接库放到lua的C路径(LUA_CPATH)中,然后在lua文件中直接使用 r ...

  9. ubuntu配置lua环境,并进行c与lua的相互调用

    1.安装lua环境 先查看一下apt可获取的lua版本 我们选择lua5.1版本进行安装 sudo apt install lua5.1 安装完之后测试一下是否安装成功,如果可以正常使用,则lua环境 ...

随机推荐

  1. Android Mvc 实现

    android studio java目录结构: Activity package目录:Controller 层 Model package目录:Model 层 View package目录:View ...

  2. Spring整合Hibernate之AnnotationSessionFactoryBean与LocalSessionFactoryBean

    spring集成hibernate由两种形式 1.继续使用Hibernate的映射文件*.hbm.xml 2.使用jpa形式的pojo对象, 去掉*.hbm.xml文件 一.继续使用Hibernate ...

  3. RabbitMQ三种Exchange

    Direct Exchange – 处理路由键.需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配.这是一个完整的匹配.如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记 ...

  4. docker 报Error: docker-engine-selinux conflicts with docker-selinux-1.9.1-25.el7.centos.x86_64

    root@ecshop Deploy]# yum -y install docker-engine-selinux.noarchLoaded plugins: fastestmirrorhttp:// ...

  5. c# Dictionary的遍历和排序(转)

    c#遍历的两种方式 for和foreach for: 需要指定首位数据.末尾数据.数据长度: for遍历语句中可以改变数据的值: 遍历规则可以自定义,灵活性较高 foreach: 需要实现ienume ...

  6. 李洪强iOS面试题之-iOS选择题

    1.及时聊天app不会采用的网络传输方式是 DA UDP B TCP C Http D FTP 2.下列技术不属于多线程的是 AA Block B NSThread C NSOperation D G ...

  7. 解决Odoo日期(时间)无效的问题 [转]

    环境Server: Ubuntu Kylin 14 + GreenOdoo-7.0-linux64, GreenOdoo-8.0-linux64客户端: winXP+firefox 31 (类似问题发 ...

  8. c#面向对象基础 类、方法、方法重载

    C#是纯粹的面向对象编程语言,它真正体现了“一切皆为对象”的精神.在C#中,即使是最基本的数据类型,如int,double,bool类型,都属于System.Object(Object为所有类型的基类 ...

  9. C#,.Net自动生成大写字母编码

    public static string GetChineseSpell(string strText)        {            int len = strText.Length;   ...

  10. BizTalk动手实验(十一)自定义开发管道组件

    1 课程简介 通过本课程熟悉自定义开始管道组件的流程.各组件接口的功能作用以及自定义管道. 本场景为开发一个消息ZIP压缩的发送管道组件. 2 准备工作 1. 熟悉管道组件各阶段组成 2. 下载Ion ...