Lua忽略范围变量

Posted

技术标签:

【中文标题】Lua忽略范围变量【英文标题】:Lua ignoring scope variable 【发布时间】:2017-10-20 05:38:15 【问题描述】:

这可能是一个愚蠢的问题,但是,我不知道发生了什么。

我有一个获取谷歌时间的简单脚本,我需要将它设置为time 全局变量。因此,在receive 事件中,我打印了获取的时间并且它工作正常。

问题是变量time 在事件外调用时总是为空。代码如下:

-- test.lua
time = ""

function getTime()
  conn = net.createConnection(net.TCP, 0)

  conn:connect(80,'google.com')
  conn:on("connection", function(conn, payload)
    conn:send("HEAD / HTTP/1.1\r\n"..
          "Host: google.com\r\n"..
          "Accept: */*\r\n"..
          "User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)"..
          "\r\n\r\n"
    )
  end)

  conn:on("receive", function(conn, payload)
    conn:close()
    time = string.sub(payload,string.find(payload,"Date: ")
       +6,string.find(payload,"Date: ")+35)
    end)
    print("testing: " .. time) -- WORKS!
end

getTime()
print("variable: ".. time)

这是我调用函数的方式(使用 nodemcu-uploader 终端):

➜  test nu terminal
--- Miniterm on /dev/cu.wchusbserial1410  115200,8,N,1 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

> dofile('lib/test.lua')
variable:
> testing: Sat, 20 May 2017 01:37:35 GMT

任何帮助将不胜感激! 谢谢

【问题讨论】:

当心!这会造成内存泄漏,因为您在 connectionreceive 回调中重用了 conn 变量。见***.com/a/37379426/131929。 @MarcelStör .. 在活动结束时设置为nil 可以解决这个问题吗? 【参考方案1】:

看起来范围很好。查看输出打印的顺序。

conn:connectcon:on 采用函数,因为它们是异步的。 getTime() 只是在它们被调用之前返回。

【讨论】:

我不知道这个..你有什么建议来处理这个吗?睡觉? @hugalves 最简单的方法是将函数传递给 getTime 并调用它,而不是设置时间。该功能可以设置时间和打印。但您的程序可能比这更复杂。 我的建议是学习基于事件的编程。使用信号和事件将使您的代码/设备更具响应性。阻塞会使坏事发生。阅读常见问题解答,它涵盖了很多内容:nodemcu.readthedocs.io/en/master/en/lua-developer-faq @TomBlodget 对不起,我不明白你的建议..你能举个代码例子吗?【参考方案2】:

NodeMCU 编程模型与 Node.js 类似,只是在 Lua 中。它是异步的和事件驱动的。因此,许多函数都有回调函数的参数。

来源:https://github.com/nodemcu/nodemcu-firmware/#programming-model

这意味着接受回调函数作为参数的函数是非阻塞的。它返回意味着您不能只逐行读取一段代码并期望它按该顺序执行。

所以,你原来程序中的事件顺序大致是这样的:

getTime 被触发但不会阻塞print("variable: ".. time) 被执行。 time 此时仍为空。 与 google.com 的连接已建立。 HEAD 请求已发送到 google.com。 响应是接收和接收事件处理程序(即匿名回调函数)启动。 time 已填充。

我看到了两个明显的修复,一个使用你的全局 time 变量,一个没有。两者都基于传递回调函数作为参数的模式。

请注意,您应该始终设置事件侦听器(在您的情况下为conn:on这些事件被触发(conn:connect)以避免错过某些事件。你的代码

conn:connect(80,'google.com')
conn:on("connection"...

仅因为conn:connect 是非阻塞的并且因为建立连接需要一些时间才有效。到这种情况发生时,on-connection 事件处理程序已经注册。

保持全局变量

time = ""

function getTime(cb)
  conn = net.createConnection(net.TCP, 0)

  conn:on("connection", function(socket, payload)
    socket:send("HEAD / HTTP/1.1\r\n" ..
            "Host: google.com\r\n" ..
            "Accept: */*\r\n" ..
            "User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)" ..
            "\r\n\r\n")
  end)

  conn:on("receive", function(socket, payload)
    socket:close()
    time = string.sub(payload, string.find(payload, "Date: ")
            + 6, string.find(payload, "Date: ") + 35)
    print("time inside on-receive: " .. time)
    cb()
  end)

  conn:connect(80, 'google.com')
end

function do_something_with_time()
  print("time inside callback: " .. time)
end

getTime(do_something_with_time)

没有全局变量

function getTime(cb)
  conn = net.createConnection(net.TCP, 0)

  conn:on("connection", function(socket, payload)
    socket:send("HEAD / HTTP/1.1\r\n" ..
            "Host: google.com\r\n" ..
            "Accept: */*\r\n" ..
            "User-Agent: Mozilla/4.0 (compatible; esp8266 Lua;)" ..
            "\r\n\r\n")
  end)

  conn:on("receive", function(socket, payload)
    socket:close()
    local time = string.sub(payload, string.find(payload, "Date: ")
            + 6, string.find(payload, "Date: ") + 35)
    print("time inside on-receive: " .. time)
    cb(time)
  end)

  conn:connect(80, 'google.com')
end

function do_something_with_time(time)
  print("time inside callback: " .. time)
end

getTime(do_something_with_time)

【讨论】:

DAAAAMN!!你摇滚!!我完全明白了!!非常感谢!!

以上是关于Lua忽略范围变量的主要内容,如果未能解决你的问题,请参考以下文章

函数/变量范围(通过值或引用传递?)

给用torch的童鞋,lua代码规范

lua学习之闭包实现原理

lua 代码风格

深入理解Lua的闭包一:概念应用和实现原理

Facebook OAuth 对话框忽略范围