Lua 意大利面条模块

Lua Spaghetti Modules

我目前正在开发自己的编程语言。代码库(在Lua中)由几个模块组成,如下:

与"A depends on B"我的意思是A中声明的函数需要B中声明的函数。

不过,真正的问题是当 A 依赖于 B 而 B 又依赖于 A 时,正如您从上面的列表中可以理解的那样,这种情况在我的代码中经常发生。

举一个具体的例子来说明我的问题,下面是 interpreter.lua 的样子:

--first, I require the modules that DON'T depend on interpreter.lua
local parser, Error = table.unpack(require("parser"))
--(since error.lua is needed both in the lexer, parser and interpreter module,
--I only actually require it once in lexer.lua and then pass its result around)

--Then, I should require memory.lua. But since memory.lua and
--functions.lua need some functions from interpreter.lua to work, I just
--forward declare the variables needed from those functions and then those functions themself:

--forward declaration
local globals, new_memory, my_nil, interpret_statement

--functions I need to declare before requiring memory.lua
local function interpret_block()
    --uses interpret_statement and new_memory
end
local function interpret_expresion()
    --uses new_memory, Error and my_nil
end

--Now I can safely require memory.lua:
globals, new_memory, my_nil = require("memory.lua")(interpret_block, interpret_espression)
--(I'll explain why it returns a function to call later)

--Then I have to fulfill the forward declaration of interpret_executement:
function interpret_executement()
    --uses interpret_expression, new_memory and Error
end

--finally, the result is a function
return function()
    --uses parser, new_fuction and globals
end

memory.lua 模块 returns 一个函数,以便它可以接收 interpret_blockinterpret_expression 作为参数,如下所示:

--memory.lua
return function(interpret_block, interpret_expression)
    --declaration of globals, new_memory, my_nil
    return globals, new_memory, my_nil
end

现在,我了解了前向声明 here and that of the functions-as-modules (like in memory.lua, to pass some functions from the requiring module to the required module) 。它们都是很棒的想法,我必须说它们非常有效。但是你付出了可读性。

事实上,这次将代码分成更小的部分让我的工作更加努力,如果我将所有内容都编码在一个文件中,这对我来说是不可能的,因为它超过了 1000 行代码,而且我' m 从智能手机编码。

我的感觉是使用意大利面条代码,只是规模更大。

那么我该如何解决由于某些模块需要彼此工作(当然不涉及将所有变量设为全局变量)而导致我的代码难以理解的问题?其他语言的程序员将如何解决这个问题?我应该如何重组我的模块?使用 Lua 模块时是否有任何标准规则也可以帮助我解决这个问题?

如果我们将您的 lua 文件视为有向图,其中顶点从依赖项指向其用法,目标是将您的图修改为树或森林,如您所愿摆脱循环。

循环是一组节点,沿顶点方向遍历可以到达起始节点。

现在,问题是如何摆脱循环?

答案看起来像这样:

让我们考虑节点 N 并将 {D1, D2, ..., Dm} 视为其直接依赖项。如果该集合中没有直接或间接依赖于 N 的 Di,那么您可以让 N 保持原样。在这种情况下,有问题的依赖项集如下所示:{}

但是,如果您有一个非空集合,例如:{PD1, ..., PDk} 怎么办?

然后您需要分析 1 和 k 之间的 i 以及 N 的 PDi,看看每个 PDi 中不依赖于 N 的子集是什么,N 的子集是什么不依赖于任何 PDi。这样你就可以定义 N_base 和 N,PDi_base 和 PDi。 N 取决于 N_base,就像所有 PDi 元素和 PDi 取决于 PDi_base 以及 N_base.

这种方法最小化了依赖树中的圆圈。但是,很可能这个组中存在 {f1, ..., fl} 的函数集,由于依赖关系而无法迁移到 _base 中,并且仍然存在循环。在这种情况下,您需要为相关组命名,为其创建一个模块并将所有函数迁移到该组中。