跟着 Lua 5.1 官方参考文档学习 Lua (6)

文章目录

    • 2.11 – Coroutines

2.11 – Coroutines

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.

You create a coroutine with a call to coroutine.create. Its sole argument is a function that is the main function of the coroutine. The create function only creates a new coroutine and returns a handle to it (an object of type thread); it does not start the coroutine execution.

When you first call coroutine.resume, passing as its first argument a thread returned by coroutine.create, the coroutine starts its execution, at the first line of its main function. Extra arguments passed to coroutine.resume are passed on to the coroutine main function. After the coroutine starts running, it runs until it terminates or yields.

A coroutine can terminate its execution in two ways: normally, when its main function returns (explicitly or implicitly, after the last instruction); and abnormally, if there is an unprotected error.

In the first case, coroutine.resume returns true, plus any values returned by the coroutine main function. In case of errors, coroutine.resume returns false plus an error message.

A coroutine yields by calling coroutine.yield. When a coroutine yields, the corresponding coroutine.resume returns immediately, even if the yield happens inside nested function calls (that is, not in the main function, but in a function directly or indirectly called by the main function).

In the case of a yield, coroutine.resume also returns true, plus any values passed to coroutine.yield. The next time you resume the same coroutine, it continues its execution from the point where it yielded, with the call to coroutine.yield returning any extra arguments passed to coroutine.resume.

Like coroutine.create, the coroutine.wrap function also creates a coroutine, but instead of returning the coroutine itself, it returns a function that, when called, resumes the coroutine. Any arguments passed to this function go as extra arguments to coroutine.resume. coroutine.wrap returns all the values returned by coroutine.resume, except the first one (the boolean error code). Unlike coroutine.resume, coroutine.wrap does not catch errors; any error is propagated to the caller.

As an example, consider the following code:

     function foo (a)print("foo", a)return coroutine.yield(2*a)endco = coroutine.create(function (a,b)print("co-body", a, b)local r = foo(a+1)print("co-body", r)local r, s = coroutine.yield(a+b, a-b)print("co-body", r, s)return b, "end"end)print("main", coroutine.resume(co, 1, 10))print("main", coroutine.resume(co, "r"))print("main", coroutine.resume(co, "x", "y"))print("main", coroutine.resume(co, "x", "y"))

When you run it, it produces the following output:

     co-body 1       10foo     2main    true    4co-body rmain    true    11      -9co-body x       ymain    true    10      endmain    false   cannot resume dead coroutine

补充:

例子:生产者和消费者

producer = coroutine.create(function ()while true dolocal x = io.read()send(x)endend
)function consumer ()while true dolocal x = receive()io.write(x, "\n")end
endfunction receive ()local status, value = coroutine.resume(producer)return value
endfunction send (x)coroutine.yield(x)
endconsumer()

We can extend this design with filters, which are tasks that sit between the producer and the consumer doing some kind of transformation in the data. A filter is a consumer and a producer at the same time, so it resumes a producer to get new values and yields the transformed values to a consumer.

例子:管道和过滤器

function receive (prod)local status, value = coroutine.resume(prod)return value
endfunction send (x)coroutine.yield(x)
endfunction producer ()return coroutine.create(function ()while true dolocal x = io.read() -- produce new valuesend(x)endend)
endfunction filter (prod)return coroutine.create(function ()for line = 1, math.huge dolocal x = receive(prod) -- get new valuex = string.format("%5d %s", line, x)send(x) -- send it to consumerendend)
endfunction consumer (prod)while true dolocal x = receive(prod)io.write(x, "\n")end
endp = producer()
f = filter(p)
consumer(f)

If you thought about Unix pipes after reading the previous example, you are not alone. After all, coroutines are a kind of (non-preemptive) multithreading. While with pipes each task runs in a separate process, with coroutines each task runs in a separate coroutine. Pipes provide a buffer between the writer (producer) and the reader (consumer) so there is some freedom in their relative
speeds. This is important in the context of pipes, because the cost of switching between processes is high. With coroutines, the cost of switching between tasks is much smaller (roughly the same as a function call), so the writer and the reader can run hand in hand.



补充:Iterators and the Generic for

7.1 Iterators and Closures

An iterator is any construction that allows you to iterate over the elements of a collection. In Lua, we typically represent iterators by functions: each time we call the function, it returns the “next” element from the collection.

Every iterator needs to keep some state between successive calls, so that it knows where it is and how to proceed from there. Closures provide an excellent mechanism for this task. Remember that a closure is a function that accesses one or more local variables from its enclosing environment. These variables keep their values across successive calls to the closure, allowing the closure to remember where it is along a traversal. Of course, to create a new closure we must also create its non-local variables. Therefore, a closure construction typically involves two functions: the closure itself and a factory, the function that creates the closure.

As an example, let us write a simple iterator for a list. Unlike ipairs, this iterator does not return the index of each element, only its value:

function values(t)local i = 0return function()i = i + 1; return t[i]end
end

In this example, values is the factory. Each time we call this factory, it creates a new closure (the iterator itself). This closure keeps its state in its external variables t and i. Each time we call the iterator, it returns a next value from the list t. After the last element the iterator returns nil, which signals the end of the iteration.

We can use this iterator in a while loop:

t = { 10, 20, 30 }
iter = values(t)       -- creates the iterator
while true dolocal element = iter() -- calls the iteratorif element == nil then break endprint(element)
end

However, it is easier to use the generic for. After all, it was designed for this kind of iteration:

t = { 10, 20, 30 }
for element in values(t) doprint(element)
end

The generic for does all the bookkeeping for an iteration loop: it keeps the iterator function internally, so we do not need the iter variable; it calls the iterator on each new iteration; and it stops the loop when the iterator returns nil. (In the next section we will see that the generic for does even more than that.)

例子:Iterator to traverse all words from the input file

function allwords()local line = io.read()                    -- current linelocal pos = 1                             -- current position in the linereturn function()                         -- iterator functionwhile line do                         -- repeat while there are lineslocal s, e = string.find(line, "%w+", pos)if s then                         -- found a word?pos = e + 1                   -- next position is after this wordreturn string.sub(line, s, e) -- return the wordelseline = io.read()              -- word not found; try next linepos = 1                       -- restart from first positionendendreturn nil -- no more lines: end of traversalend
endfor word in allwords() doprint(word)
end

A for statement like

     for var_1, ···, var_n in explist do block end

is equivalent to the code:

     dolocal f, s, var = explistwhile true dolocal var_1, ···, var_n = f(s, var)var = var_1if var == nil then break endblockendend

We call the first variable var_1 in the list the control variable. Its value is never nil during the loop, because when it becomes nil the loop ends.

The first thing the for does is to evaluate the expressions after the in. These expressions should result in the three values kept by the for: the iterator function, the invariant state, and the initial value for the control variable. Like in a multiple assignment, only the last (or the only) element of the list【explist】 can result in more than one value; and the number of values is adjusted to three, extra values being discarded or nils added as needed.

(When we use simple iterators, the factory returns only the iterator function, so the invariant state and the control variable get nil.)

After this initialization step, the for calls the iterator function with two arguments: the invariant state and the control variable. (From the standpoint of the for construct, the invariant state has no meaning at all. The for only passes the state value from the initialization step to the calls to the iterator function.) Then the for assigns the values returned by the iterator function to the variables declared by its variable list. If the first value returned (the one assigned to the control variable) is nil, the loop terminates. Otherwise, the for executes its body and calls the iteration function again, repeating the process.

7.3 Stateless Iterators

As the name implies, a stateless iterator is an iterator that does not keep any state by itself. Therefore, we may use the same stateless iterator in multiple loops, avoiding the cost of creating new closures.

For each iteration, the for loop calls its iterator function with two arguments: the invariant state and the control variable. A stateless iterator generates the next element for the iteration using only these two values.

例子:ipairs 和 pairs 函数

local function iter(a, i)i = i + 1local v = a[i]if v thenreturn i, vend
endfunction ipairs(a)return iter, a, 0
endlocal a = {"a", "b", "c" ,"d"}
for i,v in ipairs(a) doprint(i, v)
end-- The call next(t,k), where k is a key of the table t, returns a next key in the
-- table, in an arbitrary order, plus the value associated with this key as a second
-- return value. The call next(t,nil) returns a first pair. When there are no more
-- pairs, next returns nil.
function pairs(t)return next, t, nil
endlocal t = {a = 1, b = 2, c = 3}
for k,v in pairs(t) doprint(k, v)
end

7.4 Iterators with Complex State

Frequently, an iterator needs to keep more state than fits into a single invariant state and a control variable. The simplest solution is to use closures. An alternative solution is to pack all it needs into a table and use this table as the invariant state for the iteration. Using a table, an iterator can keep as much data as it needs along the loop. Moreover, it can change this data as it goes.
Although the state is always the same table (and therefore invariant), the table contents change along the loop. Because such iterators have all their data in the state, they typically ignore the second argument provided by the generic for (the iterator variable).

As an example of this technique, we will rewrite the iterator allwords, which traverses all the words from the current input file. This time, we will keep its state using a table with two fields: line and pos.

例子:重写 allwords 迭代器

local iterator -- to be defined later
function allwords()local state = { line = io.read(), pos = 1 }return iterator, state
endfunction iterator(state)while state.line do -- repeat while there are lines-- search for next wordlocal s, e = string.find(state.line, "%w+", state.pos)if s then -- found a word?-- update next position (after this word)state.pos = e + 1return string.sub(state.line, s, e)else               -- word not foundstate.line = io.read() -- try next line...state.pos = 1  -- ... from first positionendendreturn nil -- no more lines: end loop
endfor word in allwords() doprint(word)
end

Whenever possible, you should try to write stateless iterators, those that keep all their state in the for variables. With them, you do not create new objects when you start a loop. If you cannot fit your iteration into this model, then you should try closures. Besides being more elegant, typically a closure is more efficient than an iterator using tables is: first, it is cheaper to create a closure than a table; second, access to non-local variables is faster than access to table fields. Later we will see yet another way to write iterators, with coroutines. This is the most powerful solution, but a little more expensive.


补充:9.3 Coroutines as Iterators

We can see loop iterators as a particular example of the producer–consumer pattern: an iterator produces items to be consumed by the loop body. Therefore, it seems appropriate to use coroutines to write iterators. Indeed, coroutines provide a powerful tool for this task. Again, the key feature is their ability to turn upside-down the relationship between caller and callee. With this feature, we can write iterators without worrying about how to keep state between successive
calls to the iterator.

To illustrate this kind of use, let us write an iterator to traverse all permutations of a given array.

例子:生成排列

function printResult (a)for i = 1, #a doio.write(a[i], " ")endio.write("\n")
endfunction permgen (a, n)n = n or #a -- default for ’n’ is size of ’a’if n <= 1 then -- nothing to change?printResult(a)elsefor i=1,n do-- put i-th element as the last onea[n], a[i] = a[i], a[n]-- generate all permutations of the other elementspermgen(a, n - 1)-- restore i-th elementa[n], a[i] = a[i], a[n]endend
endpermgen ({1,2,3,4})

改写成协程的版本:

function printResult (a)for i = 1, #a doio.write(a[i], " ")endio.write("\n")
endfunction permgen (a, n)n = n or #a -- default for ’n’ is size of ’a’if n <= 1 then -- nothing to change?coroutine.yield(a)elsefor i=1,n do-- put i-th element as the last onea[n], a[i] = a[i], a[n]-- generate all permutations of the other elementspermgen(a, n - 1)-- restore i-th elementa[n], a[i] = a[i], a[n]endend
endfunction permutations (a)local co = coroutine.create(function () permgen(a) end)return function () -- iteratorlocal code, res = coroutine.resume(co)return resend
endfor p in permutations{"a", "b", "c"} doprintResult(p)
end

The permutations function uses a common pattern in Lua, which packs a call to resume with its corresponding coroutine inside a function. This pattern is so common that Lua provides a special function for it: coroutine.wrap. Like create, wrap creates a new coroutine. Unlike create, wrap does not return the coroutine itself; instead, it returns a function that, when called, resumes the
coroutine. Unlike the original resume, that function does not return an error code as its first result; instead, it raises the error in case of error. Using wrap, we can write permutations as follows:

function permutations(a)return coroutine.wrap(function() permgen(a) end)
end

Usually, coroutine.wrap is simpler to use than coroutine.create. It gives us exactly what we need from a coroutine: a function to resume it. However, it is also less flexible. There is no way to check the status of a coroutine created with wrap. Moreover, we cannot check for runtime errors.

例子:使用协程下载多个文件

local socket = require "socket"function receive(connection)connection:settimeout(0) -- do not blocklocal s, status, partial = connection:receive(2 ^ 10)if status == "timeout" thencoroutine.yield(connection)endreturn s or partial, status
endfunction download(host, file)local c = assert(socket.connect(host, 80))local count = 0 -- counts number of bytes readc:send("GET " .. file .. " HTTP/1.0\r\n\r\n")while true dolocal s, status, partial = receive(c)count = count + #(s or partial)if status == "closed" then break endendc:close()print(file, count)
endthreads = {} -- list of all live threads
function get(host, file)-- create coroutinelocal co = coroutine.create(function()download(host, file)end)-- insert it in the listtable.insert(threads, co)
endfunction dispatch()local i = 1local connections = {}while true doif threads[i] == nil then -- no more threads?if threads[1] == nil then break endi = 1                 -- restart the loopconnections = {}endlocal status, res = coroutine.resume(threads[i])if not res then -- thread finished its task?table.remove(threads, i)else            -- time outi = i + 1connections[#connections + 1] = resif #connections == #threads then -- all threads blocked?-- wait for any of these connections to change statussocket.select(connections)endendend
endhost = "www.w3.org"
get(host, "/TR/html401/html40.txt")
get(host, "/TR/2002/REC-xhtml1-20020801/xhtml1.pdf")
get(host, "/TR/REC-html32.html")
get(host, "/TR/2000/REC-DOM-Level-2-Core-20001113/DOM2-Core.txt")dispatch()

需要安装 luasocket 库

luarocks install luasocket

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/22718.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

位运算实用技巧与LeetCode实战

位操作&#xff08;Bit Manipulation&#xff09;有很多有趣的技巧&#xff0c;其中一个比较著名的资源是 Bit Twiddling Hacks 网站&#xff0c;它收集了各种位操作的高阶玩法&#xff0c;网址是&#xff1a; http://graphics.stanford.edu/~seander/bithacks.html 不过&…

Android输入事件传递流程系统源码级解析

1. 硬件层到Linux内核 设备节点&#xff1a;触摸事件由内核驱动捕获&#xff0c;写入/dev/input/eventX。关键结构体&#xff1a;input_event&#xff08;包含时间戳、类型、代码、值&#xff09;。 2. Native层处理&#xff08;system_server进程&#xff09; 2.1 EventHub …

【云安全】云原生-Docker(六)Docker API 未授权访问

Docker API 未授权访问 是一个非常严重的安全漏洞&#xff0c;可能导致严重的安全风险。 什么是 Docker API &#xff1f; Docker API 是 Docker 容器平台提供的一组 RESTful API&#xff0c;用于与 Docker 守护程序进行通信和管理 Docker 容器。通过 Docker API&#xff0c;…

请说明C#中的List是如何扩容的?

在 C# 中&#xff0c;List<T>是一个动态数组&#xff0c;它会根据需要自动调整其容量以容纳更多的元素。 目录 1 扩容条件与扩容算法规则 2 总结 1 扩容条件与扩容算法规则 当你创建一个新的List<T>实例时&#xff0c;如果没有指定初始容量&#xff0c;它会使…

Screen Wonders for Mac v3.3.1 3D屏保应用 支持M、Intel芯片

应用介绍 Screen Wonders 是一款专为 macOS 设计的屏保应用&#xff0c;它提供了多种高质量的动态屏保选择&#xff0c;旨在为用户的屏幕增添美感和个性化元素。 如果你厌倦了桌面上静止的图片&#xff0c;如果你准备好迎接世界各地甚至平行宇宙的魔力&#xff0c;我们在这个…

Apache Struts RCE (CVE-2024-53677)

前言 对目前的Apache Struts RCE (CVE-2024-53677)的poc进行总结&#xff0c;由于只能单个ip验证&#xff0c;所以自己更改一下代码&#xff0c;实现&#xff1a;多线程读取url验证并保存&#xff0c;更改为中文解释 免责声明 请勿利用文章内的相关技术从事非法测试&#xf…

【R语言】绘图

一、散点图 散点图也叫X-Y图&#xff0c;它将所有的数据以点的形式展现在坐标系上&#xff0c;用来显示变量之间的相互影响程度。 ggplot2包中用来绘制散点图的函数是geom_point()&#xff0c;但在绘制前需要先用ggplot()函数指定数据集和变量。 下面用mtcars数据集做演示&a…

人工智能(AI)的不同维度分类

人工智能(AI)的分类 对机器学习进行分类的方式多种多样&#xff0c;可以根据算法的特性、学习方式、任务类型等不同维度进行分类这些分类都不是互斥的&#xff1a; 1、按数据模态不同:图像&#xff0c;文本&#xff0c;语音&#xff0c;多态等 2、按目标函数不同:判别式模型…

Java 大视界 -- Java 大数据未来十年的技术蓝图与发展愿景(95)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

【网络安全 | 漏洞挖掘】账户接管+PII+原漏洞绕过

文章目录 前言正文前言 本文涉及的所有漏洞测试共耗时约三周,成果如下: 访问管理面板,成功接管目标列出的3000多家公司。 获取所有员工的真实指纹、机密文件及个人身份信息(PII)。 绕过KYC认证,成功接管电话号码。 绕过此前发现的漏洞。 正文 在测试目标时,我发现了一…

MySQL的Union和OR查询

这里写目录标题 **1. 创建表和索引****2. 编写 UNION 查询****3. 使用 EXPLAIN 分析查询****4. 分析 EXPLAIN 结果****可能的结果分析**&#xff1a; **5. 验证索引合并****总结****1. UNION 操作的分析****为什么使用临时表&#xff1f;** 2. OR 条件的分析为什么使用索引合并…

二叉排序树 -- AVL树 红黑树

手撕 – AVL树、红黑树 个人主页&#xff1a;顾漂亮 文章专栏&#xff1a;Java数据结构 文章目录 手撕 -- AVL树、红黑树1.AVL树1.1AVL树的概念1.2AVL树的性质1.3AVL树的实现 -- Java代码1.4AVL树的性能分析 2.红黑树2.1概念2.2红黑树的性质2.3红黑树的实现2.4AVL树和红黑树的比…

在 .NET 8/9 中使用 AppUser 进行 JWT 令牌身份验证

文章目录 一、引言二、什么是 JSON Web 令牌&#xff1f;三、什么是 JSON Web 令牌结构&#xff1f;四、设置 JWT 令牌身份验证4.1 创建新的 .NET 8 Web API 项目4.2 安装所需的 NuGet 软件包4.3 创建 JWT 配置模型4.4 将 JWT 配置添加到您的 appsettings.json 中4.5 为 Config…

问卷数据分析|SPSS实操之相关分析

皮尔逊还是斯皮尔曼的选取主要看数据的分布 当数据满足正态分布且具有线性关系时&#xff0c;用皮尔逊相关系数 当有一个不满住时&#xff0c;用斯皮尔曼相关系数 1. 选择分析--相关--双变量 2. 将Z1-Y2加入到变量中&#xff0c;选择皮尔逊 3. 此处为结果&#xff0c;可看我案…

自动化办公|xlwings生成图表

在日常的数据分析和报告生成中&#xff0c;Excel图表是一个非常重要的工具。它能够帮助我们直观地展示数据&#xff0c;发现数据中的规律和趋势。然而&#xff0c;手动创建和调整图表往往耗时且容易出错。幸运的是&#xff0c;借助Python的xlwings库&#xff0c;我们可以自动化…

Javascript使用Sodium库实现 aead_xchacha20poly1305_ietf加密解密,以及与后端的密文交互

Node.js环境安装 sodium-native (其他库可能会出现加密解密失败&#xff0c;如果要使用不一样的库&#xff0c;请自行验证) npm install sodium-native 示例代码&#xff0c;使用的是 sodium-native v4.3.2 (其他版本可能会有变化&#xff0c;如果要使用&#xff0c;请自行验…

【Linux】匿名管道的应用场景-----管道进程池

目录 一、池化技术 二、简易进程池的实现&#xff1a; Makefile task.h task.cpp Initchannel函数&#xff1a; 创建任务&#xff1a; 控制子进程&#xff1a; 子进程执行任务&#xff1a; 清理收尾&#xff1a; 三、全部代码&#xff1a; 前言&#xff1a; 对于管…

使用LangChain构建第一个ReAct Agent

使用LangChain构建第一个ReAct Agent 准备环境 使用Anaconda 安装python 3.10 安装langchain、langchain_openai、langchain_community &#xff08;安装命令 pip install XXX&#xff09; 申请DeepSeek API&#xff1a;https://platform.deepseek.com/api_keys&#xff08;也…

多人协同创作gitea

多人协同创作gitea 在多台设备上协同使用Gitea&#xff0c;主要是通过网络访问Gitea服务器上的仓库来进行代码管理和协作。以下是一些关键步骤和建议&#xff0c;帮助你在多台设备上高效地使用Gitea进行协作&#xff1a; 1. 确保Gitea服务可访问 首先&#xff0c;你需要确保…

【个人开源】——从零开始在高通手机上部署sd(二)

代码&#xff1a;https://github.com/chenjun2hao/qualcomm.ai 推理耗时统计 单位/ms 硬件qnncpu_clipqnncpu_unetqnncpu_vaehtp_cliphtp_unethtp_vae骁龙8 gen124716.994133440.39723.215411.097696.327 1. 下载依赖 下载opencv_x64.tar,提取码: rrbp下载opencv_aarch64.t…