记忆函数——Memorize Function

简介: 记忆函数——Memorize Function

空间换时间是一种常见的编程技巧。我们可以通过记忆函数的执行结果,在后续使用相同参数再次调用该函数时直接返回之前记忆的结果,来加快函数的运行速度。


假如有一个通用服务器,该服务器接收的请求是以字符串形式表示的 Lua 语言代码。每当服务器接收到一个请求时,它就对字符串运行 load 函数,然后再调用编译后的函数。不过,函数 load开销很昂贵,而且 发送给服务器的某些命令的出现频率可能很高。这样,与其每次收到一条诸如 "closeconnection()" 这样的常见命令就重复的调用函数 load ,还不如让服务器用一个辅助表记忆所有函数 load 的执行结果。在调用函数 load 时,服务器先在表中检查指定的字符串是否已经被处理过。如果没有,就(且只有在这种情况下)调用函数 load 并将返回值保存到表中。我们可以将这种行为封装为一个新的函数:

local results = {}
function mem_loadstring(s)
  local res = results[s]
  if res = nil then             -- 已有结果吗?
    res = assert(load(s))       -- 计算新结果
    results[s] = res            -- 保存结果以便后续重用
  end
  return res
end


这种模式节省的开销非常可观。但是,它也可能导致不易察觉的资源浪费。虽然有些命令会重复出现,但也有很多命令可能就出现一次。渐渐地,表 results 会堆积上服务器收到的所有命令及编译结果。在运行了一段足够长的时间后,这种行为会耗尽服务器的内存。


弱引用表为解决这个问题提供了一种简单的方案。如果表 results 具有弱引用的值,那么每个垃圾收集周期都会删除所有那个时刻未使用的编译结果(基本上就是全部):

local results = {}
setmetatable(results, {__mode = "v"})     -- 让值称为弱引用的
function mem_loadstring(s)
  local res = results[s]
  if res = nil then             -- 已有结果吗?
    res = assert(load(s))       -- 计算新结果
    results[s] = res            -- 保存结果以便后续重用
  end
  return res
end


实际上,因为索引永远是字符串,所以如果愿意的话,我们可以让这个表变成完全弱引用的:

setmetatable(results, {__mode = "kv"})    


最终达到的效果是完全一样的。


记忆技术还可以用来确保某类对象的唯一性。例如,假设一个系统用具有三个相同取值范围的字段 redgreenblue 的表来表示颜色,一个简单的颜色工厂函数每被调用一次就生成一个新颜色:

function createRGB (r, g, b)
  return {red = r, green = g, blue = b}
end


使用记忆技术,我们就可以为相同的颜色复用相同的表。要为每一种颜色创建一个唯一的键,只需要使用分隔符把颜色的索引连接起来即可:

local results = {}
setmetatable(results, {__mode = "v"})   
function createRGB(r, g, b)
  local key = string.format("%d-%d-%d", r, g, b)
  local color = results[key]
  if color == nil then
    color = {red = r, green = g, blue = b}
    results[key] = color
  end
  return color
end


这种实现的一个有趣的结果是:由于两种同时存在的颜色必定是由同一个表来表示,所以用户可以使用基本的相等运算符比较两种颜色。因为随着时间的迁移垃圾收集器会清理表 results ,所以一种指定的颜色在不同的时间内可能有不同的表来表示。不过,只要一种颜色正在被使用,它就不会从 results 中被移除。因此,一种颜色与一种新颜色相比已经存在了多长时间,这种颜色对应的表也存在了对应长度的时间,也可以被新颜色复用。

目录
相关文章
|
27天前
|
中间件 Docker Python
【Azure Function】FTP上传了Python Function文件后,无法在门户页面加载函数的问题
通过FTP上传Python Function至Azure云后,出现函数列表无法加载的问题。经排查,发现是由于`requirements.txt`中的依赖包未被正确安装。解决方法为:在本地安装依赖包到`.python_packages/lib/site-packages`目录,再将该目录内容上传至云上的`wwwroot`目录,并重启应用。最终成功加载函数列表。
|
2月前
|
JavaScript
箭头函数与普通函数(function)的区别
箭头函数是ES6引入的新特性,与传统函数相比,它有更简洁的语法,且没有自己的this、arguments、super或new.target绑定,而是继承自外层作用域。箭头函数不适用于构造函数,不能使用new关键字调用。
|
2月前
|
数据可视化 开发者 索引
详解Wireshark LUA插件函数:function p_myproto.dissector(buffer, pinfo, tree)
在 Wireshark 中,LUA 插件通过 `function p_myproto.dissector(buffer, pinfo, tree)` 扩展协议解析能力,解析自定义应用层协议。参数 `buffer` 是 `PacketBuffer` 类型,表示原始数据包内容;`pinfo` 是 `ProtoInfo` 类型,包含数据包元信息(如 IP 地址、协议类型等);`tree` 是
84 1
|
2月前
|
JavaScript
箭头函数与普通函数(function)的区别
箭头函数是ES6引入的新语法,相比传统函数表达式更简洁,且没有自己的this、arguments、super或new.target绑定,而是继承自外层作用域。这使得箭头函数在处理回调和闭包时更加灵活方便。
|
2月前
|
C++ 容器
函数对象包装器function和bind机制
函数对象包装器function和bind机制
21 0
|
4月前
【Azure Durable Function】PowerShell Activity 函数遇见 Newtonsoft.Json.JsonReaderException: The reader's MaxDepth of 64 has been exceeded.
【Azure Durable Function】PowerShell Activity 函数遇见 Newtonsoft.Json.JsonReaderException: The reader's MaxDepth of 64 has been exceeded.
|
4月前
|
安全 JavaScript 应用服务中间件
【Azure Function App】如何修改Azure函数应用的默认页面呢?
【Azure Function App】如何修改Azure函数应用的默认页面呢?
|
4月前
|
SQL JavaScript 前端开发
【Azure 应用服务】Azure JS Function 异步方法中执行SQL查询后,Callback函数中日志无法输出问题
【Azure 应用服务】Azure JS Function 异步方法中执行SQL查询后,Callback函数中日志无法输出问题
|
4月前
|
JSON 数据格式 Python
【Azure 应用服务】Azure Function Python函数中,如何获取Event Hub Trigger的消息Event所属于的PartitionID呢?
【Azure 应用服务】Azure Function Python函数中,如何获取Event Hub Trigger的消息Event所属于的PartitionID呢?
|
4月前
|
C++ Python
【Azure 应用服务】Azure Function Python函数部署到Azure后遇见 Value cannot be null. (Parameter 'receiverConnectionString') 错误
【Azure 应用服务】Azure Function Python函数部署到Azure后遇见 Value cannot be null. (Parameter 'receiverConnectionString') 错误

热门文章

最新文章