【问题标题】:Example usage where Lua fits much better than C/C++ [closed]Lua 比 C/C++ 更适合的示例用法 [关闭]
【发布时间】:2010-10-30 18:22:57
【问题描述】:

我目前正在嵌入 Lua 并将其用作美化的智能配置文件。但是,我认为我错过了一些东西,因为人们对 Lua 的使用赞不绝口。

例如,我可以通过显示此示例轻松解释为什么您可能使用 shell 脚本而不是 C(诚然,boost regexp 是多余的):

#include <dirent.h> 
#include <stdio.h> 
#include <boost/regex.hpp>

int main(int argc, char * argv[]) {
    DIR           *d;
    struct dirent *dir;
    boost::regex re(".*\\.cpp$");
    if (argc==2) d = opendir(argv[1]); else d = opendir(".");
if (d) {
    while ((dir = readdir(d)) != NULL) {
            if (boost::regex_match(dir->d_name, re)) printf("%s\n", dir->d_name);
    }

    closedir(d);
}

return(0);

并将其与以下内容进行比较:

for foo in *.cpp; do echo $foo; done;

您可以在 Lua 中提供任何可以让我“点击”的示例吗?

编辑:也许我的问题是我对 Lua 的了解还不够好,还不能流利地使用它,因为我发现编写 C 代码更容易。

EDIT2:

一个例子是 C++ 和 Lua 中的一个玩具阶乘程序:

#include <iostream>

int fact (int n){
    if (n==0) return 1; else
    return (n*fact(n-1));
}

int main (){
    int input;
    using namespace std;
    cout << "Enter a number: " ;
    cin >> input;
    cout << "factorial: " << fact(input) << endl;
    return 0;
}

卢阿:

function fact (n)
    if n==0 then
        return 1
    else 
        return n * (fact(n-1))
    end
end

print ("enter a number")
a = io.read("*number")
print ("Factorial: ",fact(a))

在这里,这些程序看起来很相似,但在包含、命名空间和 main() 声明中显然有些杂乱无章,您可以去掉。同时删除变量声明和强类型。

现在人们是说这是一个更大的程序的优势,还是有更多的优势?这与 bash 示例不同。

【问题讨论】:

  • 学习其他编程语言会让你成为更好的程序员。即使在 C 语言中,您也会学会以不同的方式思考。
  • 近似副本从不同的方向接近同一种问题。将它们交叉链接很好,但我自己并不认为这是“完全”复制。
  • Lua 相对于 C++ 的唯一“真正”优势是您不必编译它,并且有很多预制的 Lua 解释器可以真正集成容易地。当然,您可以使用 C++,但是您要么必须解释它,要么将文件动态链接到您的代码以使用它,这只是一种痛苦。使用 Lua,您可以设置几个方便的全局变量(或者您决定让它工作),运行一个您不必编写的解释器,然后 bam,它就可以工作了。

标签: c++ c lua


【解决方案1】:

使用 Lua 等脚本语言还有许多其他好处。

Lua 与 C++ 相比的几个优势:

  • 由于高级特性,开发时间通常更短,就像您的示例一样。
  • 改变行为不需要重新编译。
  • 可以在非开发机器上更改行为。
  • 原型制作非常快速和简单,因为您可以在运行时调整逻辑。

【讨论】:

  • ...功能可以由非开发人员修改和增强/改进
  • @none:好点子,但我仍然认为 Lua 本身就是一种编程语言,所以 IMO,你仍然需要一个“开发人员”(尽管可能经验不足)。
【解决方案2】:

尝试用 C/C++ 实现一个 Lua 表,你就会看到 Lua 的强大之处。

在 Lua 中:

a["index"] = "value"

在 C 语言中,从阅读链表开始...

C++ STL 可能会有所帮助,但它会比 Lua 更冗长。

另外,Lua 是很好的胶水。与 C 接口非常容易(恕我直言)。

【讨论】:

  • C++ 中的 std::map<:string valuetype> 怎么样?
  • 对于大型地图,std::map 的速度会慢几个数量级——你需要一个哈希地图。 Google 提供了几个 (code.google.com/p/google-sparsehash),或者您可以使用 tr1::unordered_map。或者@bugmenot77,使用 Qt 你会使用 QHash。
  • 然而,我见过的大多数 Lua 映射都很小(少于 100 个条目)。再说一次,Lua 是动态类型的,所以你需要像 std::map<:string boost:variant>. 这样的东西
  • 它不仅是动态类型的,而且任何类型的值(nil 除外)都可以用于索引表。例如,有时将函数用作键很方便。
  • 我想这强化了我的观点,以及 OP 试图提出的观点;在 C++ 中制作这样一个通用的结构是可能的,但它不是“微不足道的”,而且它肯定需要良好的 STL 知识(看看你迄今为止提出的所有 STL/Boost 结构)。 Lua 的简单在这里并不是“无能”。
【解决方案3】:

我不知道我是否会为您“点击”,但我会尝试。

嵌入 Lua 的优点之一是,您不仅可以将其用作配置文件,而且实际上可以为 lua 提供 C/C++ 接口,并通过 Lua 脚本语言“编写”它们的使用。

如果您想更改应用程序的行为/逻辑,您只需更改 Lua 脚本中的代码,无需重新编译整个应用程序。

主要用途是游戏逻辑,如 AI 或状态机,从更改到游戏的快速往返时间对于开发游戏至关重要。

当然,主要逻辑必须存在于 Lua 脚本中,而不是 C/C++ 代码中,才能有效使用。

【讨论】:

  • 谢谢。我可能夸大了一点,确实使用 Lua 在游戏逻辑中实现 AI。这样做时,我确实遇到了一些问题,即是否应该用 C、Lua 或某种混合来实现。
【解决方案4】:

关于 Lua 比 C++ 更适合哪里的示例,请查看分发脚本。 Mush Client 提供 Lua 作为脚本语言。如上面的链接所示,您可以使用 Lua 做很多事情来扩展程序。与 C++ 不同,尽管 Lua 不必编译并且可以受到限制。例如,您可以对 Lua 进行沙箱处理,使其无法访问文件系统。这意味着如果您从其他人那里获取脚本,它无法破坏您的数据,因为它无法写入磁盘。

【讨论】:

  • 谢谢。我完全可以看到 Lua 被嵌入或用于扩展、插件、脚本等的情况。但我也听说有人从 C 切换到 Lua,并且只在速度是一个因素时才编写 C“模块”。我怀疑我可能在 Lua 中不够流利,如果我是的话,我可以在 Lua 中比 C 更快/更容易地编写。
  • C++/C 也可以被沙盒和限制。看看 NaCl。
【解决方案5】:

仅用 C 语言编程可能是一项非常乏味和冗余的任务,与更抽象的高级语言相比,这当然适用。

从这个意义上说,你可以比直接在 C 中做所有事情更快地开始和完成事情,这是因为许多需要在 C 中显式手动设置、完成和清理的事情通常是隐含的,并且由诸如 Lua 之类的脚本语言自动处理(例如想象内存管理)。

同样,更多抽象的数据结构和算法通常由此类高级语言直接提供,因此如果您只需要一个标准容器(想想链表、树、地图等)。

因此,当使用相当抽象的脚本语言(例如 Lua 甚至 Python)时,您可以获得相当不错的投资回报率,尤其是当相应的语言带有良好的核心功能库时。

因此,脚本实际上是理想的想法和项目原型,因为此时您需要能够专注于您的工作,而不是大多数项目可能相同的所有机械冗余。

完成基本原型后,您总能看到如何进一步改进和优化它,可能是在 C 空间中重新实现基本的关键功能,以提高运行时性能。

【讨论】:

  • 谢谢。您是指我第二次编辑中显示的内容吗?
【解决方案6】:

Lua 作为一种编程语言的主要优点(除了可嵌入性)是

  • 以强大、高效的哈希表为主要数据结构
  • 字符串处理库在复杂性和表现力之间取得了出色的平衡
  • 一流的函数和通用的for 循环
  • 自动内存管理!!

很难找到一个简短的例子来说明所有这些。我的~/bin 目录中有 191 个 Lua 脚本;这是一个采用pstotext 的输出并连接以连字符结尾的行:

local function  printf(...) return io.stdout:write(string.format(...)) end
local function eprintf(...) return io.stderr:write(string.format(...)) end

local strfind, strlen = string.find, string.len

function joined_lines(f)
  return coroutine.wrap(function()
                          local s = ''
                          for l in f:lines() do
                            s = s .. l
                            local n = strlen(s)
                            if strfind(s, '[%-\173]$', n-1) then
                              s = string.sub(s, 1, n-1)
                            else
                              coroutine.yield(s)
                              s = ''
                            end
                          end
                        end)
end

-- printf('hyphen is %q; index is %d\n', '­', string.byte('­'))

for _, f in ipairs(arg) do
  for l in joined_lines(io.popen('pstotext ' .. f, 'r')) do
    printf('%s\n', l)
  end
end

这个例子展示了几个优点,但对表格没有任何意义。

这是来自 Key Word In Context 索引程序的简短 sn-p,它从表中获取上下文并在上下文中格式化关键字。这个例子更广泛地使用了嵌套函数,并展示了更多的表格和字符串:

local function showpos(word, pos, lw, start)
  -- word is the key word in which the search string occurs
  -- pos is its position in the document
  -- lw is the width of the context around the word
  -- start is the position of the search string within the word
  local shift = (start or 1) - 1  -- number of cols to shift word to align keys
  lw = lw - shift -- 'left width'
  local rw = cols - 20 - 3 - lw - string.len(words[pos])  -- right width
  local data = assert(map:lookup(pos)[1], "no map info for position")
     -- data == source of this word
  local function range(lo, hi)
    -- return words in the range lo..hi, but only in the current section
    if lo < data.lo then lo = data.lo end
    if hi > data.hi then hi = data.hi end
    local t = { }
    for i = lo, hi-1 do table.insert(t, words[i]) end
    return table.concat(t, ' ')
  end
  -- grab words on left and right, 
  -- then format and print as many as we have room for
  local left  = range(pos-width, pos)
  local right = range(pos+1, pos+1+width)
  local fmt = string.format('[%%-18.18s] %%%d.%ds %%s %%-%d.%ds\n', 
                            lw, lw, rw, rw)
  printf(fmt, data.title, string.sub(left, -lw), word, right)
end

【讨论】:

  • std::unordered_map、std::string、C++11 for loops 和 lambdas,以及 RAII 和 std::shared_ptr 都与 lua 的替代品一样快速且易于使用。
【解决方案7】:

脚本语言减少了构建复杂 GUI 所需的工作量,否则这些 GUI 需要大量框架粘合和重复代码。 Lua 绑定提供了几个 GUI 工具包,包括 wxWidgetsIUP toolkit

在这两种绑定中,一流的函数值和完整的闭包使事件回调易于编码和使用。

以 Lua 为核心的大型应用程序(例如 Adob​​e Photoshop Lightroom)有一个外部 C/C++ 程序,该程序托管 Lua 解释器并通过向该解释器注册 C 函数来提供对其核心功能的访问。它通常在 C 函数中实现计算密集型核心功能,但将整体流程、操作甚至 GUI 布局留给 Lua 脚本。

我在我自己的项目中发现,当与运行时加载的 IUP 以及一两个用 C 编码的基于 DLL 的自定义 Lua 模块,这些模块实现了需要该级别性能的功能,或通过其他 C 可调用库实现的功能。

我的项目的要点包括:

  • 真正的尾调用可以轻松表达有限状态机。
  • 垃圾收集内存管理。
  • 匿名函数、闭包、一等函数值。
  • 哈希表。
  • 足够丰富的字符串库。
  • Userdata 将垃圾收集器扩展到 C 端分配。
  • 元表允许丰富多样的面向对象和函数式技术。
  • 小而强大的 C API。
  • documentation,开源作为备份。
  • 通过mailing listwiki 提供良好的用户对用户支持。
  • 强大的模块,例如作者和community 提供的PEG 解析器。

我最喜欢举的例子之一是我为嵌入式系统构建的测试夹具,它需要大约 1000 行 Lua 和 1000 行 C,在 lua.exe 下运行,并使用 IUP 呈现完整的 Windows GUI。第一个版本大约在一天内运行。在带有 MFC 的 C++ 中,这至少需要一周的工作,以及数千行代码。

【讨论】:

  • 尾调用总是可以变成迭代,RAII 意味着你没有垃圾,C++11 lambdas,std::unordered_mapstd::stringstlport::rope,没有垃圾,boost::mixinsboost::spirit。你所说的没有任何东西表明 Lua 比 C++ 更好,我向你保证,我可以使用 QT 编写你的程序,而且比你用 Lua 编写它所花费的时间还要短。
  • LPEG 摇滚~~ 而且 Torch 也不错!
【解决方案8】:

我使用一个名为 Love2D 的游戏引擎,它使用 Lua 编写游戏。所有的系统调用和繁重的工作都是在一个读取 Lua 脚本的 C 程序中完成的。

使用 C 或 C++ 编写游戏时,您会发现自己在尝试处理系统的微妙之处,而不仅仅是实现您的想法。

Lua 允许“干净”的脏样式编码。

这是一个用纯 lua 编写的游戏对象的示例:

local GameObj = {}              -- {} is an empty table
GameObj.position = {x=0,y=0}
GameObj.components = {}

function GameObject:update()
    for i,v in ipairs(self.components) do    -- For each component...
        v:update(self)                       -- call the update method
    end
end

实例化:

myObj = setmetatable({},{__index=GameObj})
-- tables can have a meta table which define certain behaviours
-- __index defines a table that is referred to when the table
-- itself doesn't have the requested index

我们来定义一个组件,键盘控制呢? 假设我们有一个为我们输入的对象(将由 C 端提供)

KeyBoardControl = {}
function KeyBoardControl:update(caller)
    -- assuming "Input", an object that has a isKeyDown function that returns
    -- a boolean
    if Input.isKeyDown("left") then
        caller.position.x = caller.position.x-1
    end
    if Input.isKeyDown("right") then
        caller.position.x = caller.position.x+1
    end
    if Input.isKeyDown("up") then
        caller.position.y = caller.position.y-1
    end
    if Input.isKeyDown("down") then
        caller.position.y = caller.position.y+1
    end
end
--Instantiate a new KeyboardControl and add it to our components
table.insert(myObj.components,setmetatable({},{__index=KeyboardControl})

现在当我们调用 myObj:update() 时,它会检查输入并移动它

假设我们将大量使用这种带有 KeyboardControl 的 GameObj,我们可以实例化一个原型 KeyObj 并像继承对象一样使用它:

KeyObj = setmetatable( {}, {__index = GameObj} )
table.insert(KeyObj.components,setmetatable( {}, {__index = KeyboardControl} )    

myKeyObjs = {}

for i=1,10 do
    myKeyObjs[i] = setmetatable( {}, {__index = KeyObj} )
end

现在我们有了一个可以玩的 KeyObj 表。 在这里,我们可以看到 Lua 如何为我们提供了一个强大、易于扩展、灵活的对象系统,它允许我们根据我们试图解决的问题来构建我们的程序,而不是必须弯曲问题以适应我们的语言.

此外,Lua 还具有其他一些不错的特性,例如作为一等类型的函数,允许 lambda 编程、匿名函数和其他通常让 comp-sci 老师笑得令人毛骨悚然的东西。

【讨论】:

    【解决方案9】:

    LUA 有closures,并且闭包摇滚。对于example

    function newCounter ()
      local i = 0
      return function ()   -- anonymous function
               i = i + 1
               return i
             end
    end
    
    c1 = newCounter()
    print(c1())  --> 1
    print(c1())  --> 2
    

    您可以创建一个函数并传递它。有时它比创建单独的类并实例化它更方便。

    【讨论】:

    • C++ 也是如此。请参阅 C++11 和 C++14 lambda。
    猜你喜欢
    • 2015-06-12
    • 1970-01-01
    • 1970-01-01
    • 2017-04-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-06
    • 1970-01-01
    • 2010-10-11
    相关资源
    最近更新 更多