免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: __BlueGuy__
打印 上一主题 下一主题

[C] Lua 造成的代码冗余太严重了, 这个现状怎么改善? [复制链接]

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
31 [报告]
发表于 2012-11-18 00:48 |只看该作者
回复 25# OwnWaterloo


    好吧,先澄清一点:Lua的数据本身除了其“独立性”以外,没有其他的别的区别了,对程序员来说,就本质而言,io.open返回的东西和coroutine.create返回的东西,程序员是完全看不出区别的。说白了大家都是void*。所以我才说Lua的接口只有函数。file或者coroutine,只是函数在某个不透明指针上的表现而已,本质上还是函数作为接口,file或者coroutine只是函数的**上下文**。

然后就是,Lua模块的惯例(从Roberto和Luiz发布的模块作为标准,Lua社区默认执行)是,C模块,比如xxx.dll,所有导出的接口都是公开的。即xxx.foo,xxx.bar,只要有,就是公开的,而对于Lua模块,只有主接口是公开的。除非文档说明。Python也是一样的(主接口是__init__.py,其他的是否公开由文档说明,默认是不给你用的),任何对Lua有一定了解的人都知道这个规则——————问题是,你看看这个帖子吧,很多人对Lua一知半解就开始用,你让我怎么说好??

我问你,char*类型本身不管指向的内存是啥,要分配内存才能用,但是有些人就是对C一知半解,然后写:

char *buff;
strcpy(buff, "hello");

然后执行成功了!然后就一直这么用了,我问你这种人你怎么办?你怎么办?稍微学了一点C的人都不会这样吧?都知道这样的问题吧?问题就是用Lua的很多人对Lua的了解就只到这个水平就开始用Lua,你是怪Lua还是怪人呢?

现在的C编译器对这种用法有警告了(buff未初始化),但是Lua到这一步还有很长的路要走,其实也是有警告的,LuaInspect会给这种警告,但是刚刚学Lua的人又怎么知道LuaInspect的存在呢?就如同刚刚学C的人,又怎么看得懂C编译器给出的警告呢?(未初始化?什么叫未初始化?不懂,算了不管他了……)

Python的import的确会导致泄漏问题。比如说你有个os模块,你在里面会用到sys模块的内容:

# os.py
import sys
....

然后你载入os模块
import os
dir(os)

你会发现里面有个项叫"sys"……对,就是这样……你自己去看Python2.7,现在还是这个样子的!然后有些人就可能把sys当作os的某个interface来用了,我最开始的时候也干过这个事儿!但后来才知道,这实际上这只是个实现手段而已!Lua就不会有这样的问题……

是否默认导出神马的,一个良好实现的Lua,是根本不会有这样的问题的。导出的只有函数,而没有上下文,上下文由new给出,用setmetatable和函数绑定。以这种方式使用是不存在有“私有内容不允许你用”这种问题的。最多就是这个模块的接口没有稳定。大多数的动态语言其实都有这个问题。Lua对此也没有语言层面的解决方案(连模块系统都不是语言层面的,你还想要什么?)

就比如C对char*到底是否指向一个合法的地方是没有语言层面的强制方案的,你必须自己遵守规则,而且这些规则还有些暧昧不清(pm的那个关于二维数组的帖子)。你是可以说是C不好,可是事实就是这样,你咋办?你说语言不好?然后你就自己去发明一个?(你现在就在这么做了)

搞清楚一门语言的哲学和折中吧。不遵守语言的游戏规则,你怎么做都是错。

论坛徽章:
5
技术图书徽章
日期:2013-08-17 07:26:49双子座
日期:2013-09-15 16:46:29双子座
日期:2013-09-25 08:17:09技术图书徽章
日期:2013-09-25 09:11:42天秤座
日期:2013-10-01 16:25:34
32 [报告]
发表于 2012-11-18 00:52 |只看该作者
本帖最后由 __BlueGuy__ 于 2012-11-18 01:03 编辑
OwnWaterloo 发表于 2012-11-18 00:34
我觉得你不适合走纯技术路线。。。
这话难听。而且我这判断的依据也只是你在CU的种种表现。哪怕采样 ...


我也这样觉得,
没办法啊,为了混口饭,不适合也要杠下去啊,

论坛徽章:
5
技术图书徽章
日期:2013-08-17 07:26:49双子座
日期:2013-09-15 16:46:29双子座
日期:2013-09-25 08:17:09技术图书徽章
日期:2013-09-25 09:11:42天秤座
日期:2013-10-01 16:25:34
33 [报告]
发表于 2012-11-18 00:57 |只看该作者
本帖最后由 __BlueGuy__ 于 2012-11-18 01:00 编辑
fergon 发表于 2012-11-18 00:34
这些年做网游的不是都捞得风生水起么?为什么经你二人一说,都成了苦逼了?


失败的游戏很多的, 很多公司连名字你都没听说过,就已经死了
有的投了2亿,员工会议上自己把自己说的都感动的哭了, 最后还是死了...

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
34 [报告]
发表于 2012-11-18 02:10 |只看该作者
starwing83 发表于 2012-11-18 00:48
好吧,先澄清一点:Lua的数据本身除了其“独立性”以外,没有其他的别的区别了,对程序员来说,就本质而言,io.open返回的东西和coroutine.create返回的东西,程序员是完全看不出区别的。说白了大家都是void*。所以我才说Lua的接口只有函数。file或者coroutine,只是函数在某个不透明指针上的表现而已,本质上还是函数作为接口,file或者coroutine只是函数的**上下文**。

close("file")不行而close(open("file"))可以,这种区别总是存在。
至于它叫不叫类型,无所谓了。。。 你说是"上下文",那就是上下文,可以了吧?
而且。。。 其实是否能导入导出这种东西与我前面的问题也根本没有关系。。。 我不该提到Ref的,我错了,行么? 这种文字游戏可以终止了么?


starwing83 发表于 2012-11-18 00:48
Python也是一样的(主接口是__init__.py,其他的是否公开由文档说明,默认是不给你用的)

Python的import的确会导致泄漏问题。比如说你有个os模块,你在里面会用到sys模块的内容:
# os.py
import sys
....

然后你载入os模块
import os
dir(os)

你会发现里面有个项叫"sys"……对,就是这样……你自己去看Python2.7,现在还是这个样子的!然后有些人就可能把sys当作os的某个interface来用了,我最开始的时候也干过这个事儿!但后来才知道,这实际上这只是个实现手段而已!Lua就不会有这样的问题……

嗯,是这样。

starwing83 发表于 2012-11-17 23:26
回复 21# OwnWaterloo

(不像python,你import出来的库外界是看得到的)

我以为"外界看得到"的意思是import os, 于是就可以dir(sys)了。 这就太搓了。



至于有多少人对C一知半解,这个帖子里有多少人对lua一知半解什么的。。。
其实我不关心他们是不是真的一知半解, 也不是在为他们辩护

参与到这个帖子是因为:
1. Haskell,或者说ghc支持这样的组织方式。
我就遇见过这样的事, 对一个module的某方面的功能感兴趣, 于是就去看源代码, 于是发现这些功能与它导入的另一个module有关。
然后我就自己import那个module, 然后ghc告诉我不行, 因为这个module是hidden的,需要一个编译参数才能import。

于是,库的内部也可以使用模块机制来组织代码,并且这种组织方式默认对库的用户是不公开的。

无论什么语言、什么软件、什么社区有它自身的什么哲学, 如果它们忘记了模块间的核心交互界面是"less is more"这一哲学, 那那些哲学就是渣渣。
如果语言、或者说编程环境能为less is more提供帮助, 比如ghc的hidden module, 那就是值得考虑的。

提供hidden module这样的机制能让程序员更清楚地表达他们写的模块间的接口, 让这些模块间更容易满足less is more。
但另一方面, 这又与程序员与编程工具之间的less is more违反了。
所以这里就有折衷: 到底应该怎样在程序员与编程工具之间提供hidden module这样的机制?
是应该ghc那样在reference implementation里? 还是更强烈一些, 在语言规范里? 还是更弱一些,在辅助工具里? 还是再弱一些, 在编程规范里?

2. 在el和python里我都干过通过相对路径载入的事情

相对路径可以达到类似hidden module的功能,比如这样的目录结构:

  1. lib
  2. lib/src/a.el
  3. lib/internal/b.el
复制代码
a.el的内容是:

  1. (load "../internal/b")
  2. ...
  3. (provide 'a)
复制代码
将lib的仓库clone到本地, 然后将 lib/src加入到load-path。 然后(require 'a)。
不存在19楼说的重复的问题。 重复是在(require 'a)的时候被处理的, 而且用户想直接加载b.el反而是很不容易做到的。
当然, el只有一个名字空间是有实用性的使得这方法在el里也不太好用。。。

也许,这只是我的猜测,鹿子想要的是hidden module,所以就采用相对路径这一不native的方式去达成了。
不管鹿子到底是怎么想的, 因为他的回帖我突然想到这点了, 于是就来请教下这在lua里有没有native的方式达成?


3. 用相对路径还干过一件事。。。  刚想起的。。。

  1. lib/init.el
  2. lib/plugin/
复制代码
init.el会加载plugin下的所有.el文件。  无论到底有多少plugin, 只要目录结构不变, init.el就不需要改一行代码。

这在lua里又有什么native的方法么?


4. lua有reload功能么? 如果有, 能分析dependences么?  应用场景我也不想写了。。。
总之,相对路径加载是很有用的功能。 如果lua的最native做法就是19楼那样。。。  我也不知道说什么好了。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
35 [报告]
发表于 2012-11-18 09:45 |只看该作者
回复 34# OwnWaterloo


    Lua这种语言,不可能过多的把精力关注在模块系统上。Haskell的module,import都是语法。Lua怎么办呢?

实际上,Lua5.1的require允许你传递额外参数,所以如果你真就喜欢隐藏参数了,这么干吧…………

主模块:

local function private(module)
   if name_is_private(module._NAME) then error("private module") end
   return module
end

然后载入的时候:

require("xxxx", private)

不过这个特性在5.2被去掉了,所以……………………

更严重的是,无论你做什么手脚,无论你是不是私有的,模块载入这个行为是全局的,这就意味着,无论是否私有化,无论是否本地化,无论你用任何方式载入的这个库,只要你载入一遍了,你就可以在毫不修改任何默认参数的情况下require第二遍。因为被载入的库会被缓存,第二次载入根本不会再进入模块载入功能了,会直接加载缓存的那个库。也就是说,相对路径载入,根本达不到hide module的功能。你最终载入了plugin.foobar,用户会发现他突然能载入一个“神秘”的模块,叫做plugin.foobar了,而且很好用!突然有个版本这个神秘模块没了,用户傻逼了。

3的答案是不行,Lua没有遍历路径的任何方式。因为ANSI C没有。

4reload理论上做得到,云风就做到了。但是那意味着必须非常非常小心地写模块和框架,要有非常严格的语法限制,具体要花时间总结,而且有一点很重要的是,标准模块机制没用了。或者至少是得做大幅度修改。

我想说说我的观点。BASIC里面有goto,确切地说,1980年的GW-BASIC里面**只有**goto,所以产生了不少面条代码。所以你说goto是坏的,goto是恶魔。到了2012年的今天,有人听到了“goto是恶魔”这句话,于是就说“C语言里面goto是恶魔”,愚昧不?

C里面指针乱指导致内存泄露,于是人们说“Haskell内存不安全,指针乱指容易泄露”,傻逼不?

为什么一门语言的缺陷,就必须是所有语言的缺陷呢?

C模块容易造成安全性的私有模块的问题,仅仅在C模块——因为那在Lua能力范围之外。所以,你有选择是否导出的权利——确切地说,你只有明确地导出才能用。这避免了你喝醉了酒误加了一个参数然后把某个模块公开了——这不可能。你至少得写一打代码做导出。

而Lua模块。为什么我最开始说Lua导出的只有函数?是因为Lua的函数不危险。它们没有上下文。或者说,他们的上下文是死的,是文件里面写死的。Lua的模块机制是“平”的。即使你写a.b.c.d.e.f,到了module.loaded里面也是平的。平的模块机制是无法解决隐藏模块的问题的,也不需要解决隐藏模块问题。Lua无意隐藏任何模块。因为你只要拿出来的模块,就可以公开,出问题了是谁的问题呢?前面说了,社区啥啥啥的。语言为什么要强制某些东西呢?特别的,在Lua这种连参数数目都不强制,连变量声明都不强制的语言里面,你去强制不允许载入某些模块,你不觉得很可笑么?

Lua的哲学是“平面的比递归的好,显式的比隐晦的好”。所以Lua的模块机制是扁平的,Lua的模块载入是显式的(不允许一次载入多个模块如import ..plugins.*这种)。但是,Lua的哲学还有一个“做得到比做不到好”,因此你可以自己实现你想要的模块载入机制——啊啊啊,我知道你想说什么事情了。“这不标准”“你不能强制每个人都用你写的模块载入”,对不对?两个错误。1.Lua的模块机制对模块的要求是最低的。唯一要求是“你得返回你的模块内容”,Over,因此任何人实现的任何模块载入机制,都兼容所有人写的Lua模块。2.事实上有人已经做了,而且得到了官方承认。Lua邮件列表大概一两个月前很热烈的讨论了require的诸多可能,好像还创建了一个项目叫llrequire还是啥的,反正加了很多他们(自以为)很帅的功能,其中就有你的reload——虽然这不现实。

reload的问题,详细说说——你得保证所有模块的使用不会被缓存。就这么简单。但是也不简单。这意味着效率无法得到保证。无论中间加抽象层,还是用查表法调用函数,效率上都无法保证。所以我倾向于不依赖reload,或者在特定的机制上(仅依赖少数接口)的模块进行reload。

Lua的第一设计目标是嵌入式语言,简单是压倒一切的要求。第二设计目标里面,Lua的哲学阻止了实现所谓的私有模块和一次载入一户口本的模块。第二设计目标里面,Lua哲学允许你自己实现这些东西,而且和通用模块兼容,你还想要什么呢?——哦,对了,Lua还提供了实现这些东西的“砖瓦”,无论是package.loadlib(载入dll文件)还是package.searchpath(搜索一系列路径)都给你了,简直就是司马昭之心路人皆知啊………………那你还不动手?

其实自己设计一套载入机制也没什么太大问题,但这是一种很危险的信号:你在将其他语言的经验放入Lua。为什么不按照Lua的思路来,显式地、平面地去载入模块,不要去载入自己不知道的模块呢?如果你知道某个模块,你载入了他,下个版本他消失了,你难道不能从旧版本拷贝一个,放回来么?谁阻止你这么做了呢?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
36 [报告]
发表于 2012-11-18 14:29 |只看该作者
回复 35# starwing83

>>     Lua这种语言,不可能过多的把精力关注在模块系统上。Haskell的module,import都是语法。Lua怎么办呢?

lua不可能有上百M的编译器。现在没有,可以预见以后也不会有。但苦衷归苦衷,没有就是没有。

C++标准没法强制各种编译器采用统一的mangling, 这是苦衷, 但没有就是没有。
总不能因为C++没有统一的mangling就说dlsym(m, "function_written_in_cpp")这种需求是错的。
但C++至少还是提供了extern "C",可以绕着道让dlsym成功。

而在更多二进制接口层面,C++更是无力。
只要一个模块敢往外抛异常,使用这个模块的用户的编译器集合就会被限制
只要另一个模块敢往外传递non pod,使用这个模块的用户的编译器集合就还是有限制。
理论上,这些集合的交集可能为空。。。  实际上我没遇见过。 因为发现这问题后, 就开始在模块边界限制使用这些东西了。
最终结果你也知道,就是对C++的特性从尽可能多用转变到尽可能少用。

二进制异常spec在C++里代价很高,基本没有希望了。
而extern "C"用于导出,对C++实现(但对写代码的人来说依然很麻烦)来说可能是0代价的,因为它本来就要用它实现导入。导入是必须支持的。
因为什么东西支持的代价很高,甚至根本不可能会提供, 因此这些需求就是错误的? 错在需求, 而不是语言自身的各种苦衷?

lua的dofile(注意不是require),如果要支持相对路径,又会有多大代价???

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
37 [报告]
发表于 2012-11-18 14:52 |只看该作者
starwing83 发表于 2012-11-18 09:45
回复 34# OwnWaterloo

更严重的是,无论你做什么手脚,无论你是不是私有的,模块载入这个行为是全局的,这就意味着,无论是否私有化,无论是否本地化,无论你用任何方式载入的这个库,只要你载入一遍了,你就可以在毫不修改任何默认参数的情况下require第二遍。因为被载入的库会被缓存,第二次载入根本不会再进入模块载入功能了,会直接加载缓存的那个库。也就是说,相对路径载入,根本达不到hide module的功能。你最终载入了plugin.foobar,用户会发现他突然能载入一个“神秘”的模块,叫做plugin.foobar了,而且很好用!突然有个版本这个神秘模块没了,用户傻逼了。


从18到19楼,你对鹿子的需求就提出了一点改变。  我前面的回帖里也有变相提到这点。  不过,看来还是得明确说出来。。。

在你的概念里: 模块载入==require? 思维开放点嘛。。。 dofile就不能用来加载模块? 因为它没有"缓存"?
而你在这个回复里已经自己推理出有些时候这个缓存反而碍事。

stm由atomic构成,并且stm作者不愿意将atomic作为共有接口。理想情况stm.atomic应该对外根本就不可见。
如果stm.lua里用dofile,让用户去require stm。 是不是就没有神秘模块了? 而且也不会污染package.loaded等东西(我前面问的"全局设置"之一)。
而且也没有重复加载问题。
对用户来说, stm就像是写在一个stm.lua文件里一样, 只有这一个lua文件, 没有别的。 stm.lua里的dofile只是它自己的组织方式。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
38 [报告]
发表于 2012-11-18 15:00 |只看该作者
starwing83 发表于 2012-11-18 09:45
回复 34# OwnWaterloo

3的答案是不行,Lua没有遍历路径的任何方式。因为ANSI C没有。


因为ansi c没有,所以lua不提供,所以包含路径遍历的应用程序都与lua无关?
显然不可能。


你是不是将前面:

  1. lib/init
  2. lib/pluggin
复制代码
的意思理解成dofile("pluggin/*")什么的了?
不是。 不是要求dofile自己就有wildcard功能。 wildcard是wildcard的事, 只要求它有相对路径就好办了。 支持这个代价会很高么?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
39 [报告]
发表于 2012-11-18 15:20 |只看该作者
starwing83 发表于 2012-11-18 09:45
回复 34# OwnWaterloo

4reload理论上做得到,云风就做到了。但是那意味着必须非常非常小心地写模块和框架,要有非常严格的语法限制,具体要花时间总结,而且有一点很重要的是,标准模块机制没用了。或者至少是得做大幅度修改。


吓死我了,这是在说什么?

我说的reload,指interactive环境里的work flow。
不再是edit -> [compile -> ] run -> debug这样的循环。不是file base的开发流程, 而是repl oriented的。
file不再作为编译/执行的单位, 它存在的目的就是将repl给persistent化。。。 作为一种介质,可以复现某个时刻的repl环境。。。

通常,可以随时以函数为单位执行。也可以以函数为单位替换它的定义。
但有时候,比如重写的函数多了,又或者进行了诸多的修改后想测试一下,就可能会reload整个文件。

如果没有dependences分析的话,内部模块用没有去重功能的load,外部用有去重的require反而更方便。
这时候就是想把依赖的内部模块给重新load,不要理会缓存。

而且对最终用户还更好。因为对package.loaded这样的东西(features, class path)的污染还更小。


BTW: 我只是顺带提一下。 lua是不是支持对我来说完全无所谓。 反正我目前又不用lua。

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
40 [报告]
发表于 2012-11-18 15:34 |只看该作者
回复 36# OwnWaterloo


    这不是代价不代价的问题。dofile的执行环境本身可能根本就不在某个file里面。dofile需要自己去查找这个函数的可能file再去判断啥的。理论上也做得到(你看到了),但是代价是很大的。更何况5.2的新功能(调试信息可能被strip掉),就更加不可能了。你见过C里面有个函数能在运行时取字符串name所指代的变量内容的么?没错,有调试信息的话做得到,但是没有呢?dofile面临的基本上就是这个问题。

Python为什么做得到?因为这个货所谓的strip模式strip掉的是注释……注释……其他的我就不多说什么了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP