免费注册 查看新帖 |

Chinaunix

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

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

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
81 [报告]
发表于 2012-11-18 19:51 |只看该作者
starwing83 发表于 2012-11-18 18:39
这啥逻辑?你这除了让我承认“哎呀Lua好搓啊,哎呀我咋就用这么挫的东西写了这么久的代码捏?”?是不是我承认以后你还得加一句“是啊是啊,你看你多搓,你看你的思维全被Lua给限制住了!!赶紧皈依我佛吧!”,这算啥?


我就是想问该如何实现那种组织方式。 你一开始就明明白白说"默认情况只有模块名是公有的,除非文档说明", 就完事了。
你自己往前翻: 1) 文档代码 2) 约定俗成 3) 辅助工具 4) 参考实现 5) 语言规范 —— 由弱到强, 随便那一条, lua有就行。
现在看来是 2。


非要一会给我说哲学、 一会给我说思想、 一会给我说精髓。 总之lua不可能有错, 错都是不理解lua的人的错。 我烦。

你思维又是不是被限制住了? 是不是任何事都从lua和vim的经验出发?
我就这么给你提一下, 既然你听不进去, 那是我错了, 你没有, 你的思维是开放的。 这是我的错, 可以了吧?

论坛徽章:
6
寅虎
日期:2013-10-10 21:59:16狮子座
日期:2013-11-12 09:24:41金牛座
日期:2013-12-14 17:02:23酉鸡
日期:2014-01-16 12:34:37技术图书徽章
日期:2014-02-15 12:52:31巨蟹座
日期:2014-05-17 14:09:52
82 [报告]
发表于 2012-11-18 19:53 |只看该作者
回复 78# OwnWaterloo


    yesod , 我正在学习它的入门教材,把它作为学习和熟悉Haskell的练手。你觉得我这种学习方式靠谱吗?

论坛徽章:
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
83 [报告]
发表于 2012-11-18 19:54 |只看该作者
回复 80# OwnWaterloo


    那抱歉,估计是我白痴不懂啥叫异常处理。麻烦科普一下,究竟满足啥条件的系统,才能叫做“支持异常处理”呢?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
84 [报告]
发表于 2012-11-18 19:56 |只看该作者
starwing83 发表于 2012-11-18 18:39
最后一个回复:我用repl的经验一直很糟糕。因为你不是Vimer你体会不到。Vim的文件编辑是立体的,是跳脱的。在repl我感觉自己就被限制住了。我打个比方吧。GW-BASIC,以前在文曲星,甚至学习机上经常玩。现在看来实在是很操蛋。但是那种“操蛋”的REPL就是立体的,比如说这样:

list
10 print "hello"
OK

然后,你将光标挪到10那一行,把10改成20,回车,然后程序就变成了2行!明白吗?是很灵活的可以对屏幕上任意文本进行修改并且执行的。我可以很快的在Vim里面产生一个几行的表格,我可以随时看到语法高亮的结果,我可以不写括号,而等到写完以后统一加上,然而在repl里面,我感觉所有的高级功能,那些跳跃,那些preview都离我而去了,我只能想在Emacs里面那样,老老实实地敲代码,f功能没了,*功能,#功能,d,p,c,v这些都没了,你明白么?


然后,将你编辑的结果,送给(send)repl。 repl是怎么限制你的体验、你的高级功能、你的跳跃、你的preview的?
编辑归编辑的事, 将若干代码送给repl执行是send的事, 这两者是怎么牵连上的? 难道不是因为vim的shell功能弱?

论坛徽章:
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
85 [报告]
发表于 2012-11-18 20:02 |只看该作者
回复 84# OwnWaterloo


    那你有啥办法?转Emacs?Emacs的shell是够了,但是编辑那就是渣啊………………

难道非得等VimE写完么……现在我手上这个版本的shell直接是渣 = =|||

PS:Emacs的shell也好不到哪儿去吧?如果我没记错的话,貌似是无法让isatty返回1的吧?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
86 [报告]
发表于 2012-11-18 20:35 |只看该作者
starwing83 发表于 2012-11-18 18:39
我完全不理解repl所谓persistent,看来你也不打算科普了。事实上我从没用到过repl的persistent的能力。我也不知道reload到底是什么含义,当然你也不打算解释了。所以你就是来秀你的优越感的?

不是我不打算科普, 我是预感你不会感兴趣, 因为你有个啥啥啥写到代码里的东西。


starwing83 发表于 2012-11-18 18:39
那是,我也不知道所谓的基于repl的开发,而不是基于edit-compile-eval的开发到底有啥优越感可言,你嘲笑我好了。我就只会edit-compile-eval。对我来说这真的好不方便好难啊,我得按gc才能执行正在编辑的文件呢!

果然我没猜错吧。



好嘛,那就科普嘛。。。
程序由一堆function definitions构成(嗯,前面你也说了,接口只有函数)。
这些definition是否真的正确? 总得调用了才知道。
于是就会有一个启动用的文件、模块、函数等等, 由它的执行去调用、去间接调用构成这个程序的各个function definitions。
就像haskell, 是将(几乎)所有的IO转移到main, 最终由main启动, 去执行那些IO操作。

除了主程序, 可能还会有一堆测试程序。(如果是开发程序库, 可能就没有主程序, 只有这一堆测试程序了)。
终归它们的目的就是要去调用那些function definitions。
总归它们是 edit files -> [compile files ->] run startupfile 的循环。
而repl可以将粒度从file降低到function。 将周期也从edit file -> run 缩短到 edit function -> try new definition。
最主要目的就是缩短反馈时间, 没别的了。  lua想做也能做。 所以我前面都不太想提这点, 有它没它不会有特别大的变化。


微小的改进也是改进。 假设是一个long run的程序(server, gui)。 可以先搭出程序的架子, 然后启动程序。 就这一次。
然后一边改各种handler,一边将修改送出, 就可以从这程序(进程)得到反馈。 可以省去很多很多次重新启动这个程序。

整个emacs就是这样一个程序。 写/修改一个函数, 送给emacs执行了(C-x C-e)就可以立马得到反馈, 不需要重启emacs。
是不是没看到终端提示符就觉得这不是repl了?
emacs的command loop就是loop, command loop中有一个命令就可以从buffer中读取(read)一个expresson, 然后执行(eval), 然后输出(print)到echo area(或者插入到buffer中)。

对于server, remote repl可以起到怎样的作用我还在体会中。


不要说这是因为jvm启动慢而想出的变招。。。
这东西不是clojure发明的。 它原先就存在了,可以用来缩短development circle。 只是对于clojure,顺带也可以缓解jvm启动慢。



最后说repl和reload的关系。 关键点是只有写在文件里的代码才是persistent的, 才是软件。 repl process是一个短暂的。

想持久化的definition就写到文件里去, 比如架子代码。
想改可以就在文件上改, 但不需要重新启动一个process并加载这个文件以及它的依赖才能看到修改的效果。
可以在将修改后的代码送给repl后就直接尝试效果。
这些尝试, 如果觉得有重复尝试的必要, 就可以写到另一些文件里, 就是测试代码了。 总之文件里的代码才是persistent, 可复现的。

架子代码中的某些函数可能会拆分为更多的函数。
如果没考虑好怎么拆分, 可以将这些新函数写在项目外的文件里, 或者写在没有对应文件的buffer里, 甚至直接写在repl的prompt里。
成熟了再搬回项目文件里, 才能被复现。

理想情况下, repl process里包含的definitions应该与项目文件里包含的是同步的。实际上达不到这点。
过一段时间可能就需要重新加载文件, 让文件里的definitions与repl process中的definition同步。 甚至可能还需要将某些库先unload, 然后再load。 比如删除了某些definition的时候。
clojure也有工具全自动或半自动的维护这种同步。 我暂时还没精力去弄。
ghc做得更好一些, 但因为haskell不是expression oriented的, 所以不是什么东西都可以送给repl执行的。


如果要在lua里也这样做, require的避免重复加载反而是件麻烦事情。 因为这个时候就是想要重复加载, 使得两者同步。
相对于实现dependences分析, 我觉得写dofile更实际一些。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
87 [报告]
发表于 2012-11-18 20:50 |只看该作者
starwing83 发表于 2012-11-18 18:39
- Lua本身先不管做不做得到了,这货很简单粗暴,所以也没那么多好争的。语法上的灵活性导致几乎所有想法都能做到,如果你要争论是否是官方的是否是约定俗成的那我也没办法。
- 我并不认为追求简单就是在固步自封。我就是不喜欢上百M的编译器。
- repl我用过,但是感觉没有在editor里面那么灵活,你也是windows,你怎么能闭着眼睛说瞎话呢?当一个函数很长,长到了prompt要显示PS2的时候,万一你打错了,回车的结果是error的后果是什么?是一行一行翻过去重新按回车,然后把错的地方改对!那我为什么不能在editor里面一次改对,然后执行呢?这样不是更快么?clojure的repl我就遇到过这种痛苦事儿,写fac写了三行,一按回车给了个错,然后我发现某括号没对准,于是我翻到第一行,回车,翻到第二行,回车,翻到第三行,光标移动到最后,加),回车,好了,在Vim里面我早就在试验第三种写法了!!
- 别有事没事给人扣帽子。别人说不是这个东西的错,就是固步自封?非要让别人说这个东西不好这个东西不对这个东西生来就是错?你这叫啥来着?

就是这样。


>> repl我用过,但是感觉没有在editor里面那么灵活,你也是windows,你怎么能闭着眼睛说瞎话呢?当一个函数很长,长到了prompt要显示PS2的时候,万一你打错了,回车的结果是error的后果是什么?是一行一行翻过去重新按回车,然后把错的地方改对!那我为什么不能在editor里面一次改对,然后执行呢?这样不是更快么?clojure的repl我就遇到过这种痛苦事儿,写fac写了三行,一按回车给了个错,然后我发现某括号没对准,于是我翻到第一行,回车,翻到第二行,回车,翻到第三行,光标移动到最后,加),回车,好了,在Vim里面我早就在试验第三种写法了!!

你还不承认这是因为vim的shell弱导致你对repl的印象就是在终端里而不是编辑器里输入代码???思维僵化这帽子扣对没有?

你很多评测的依据都是被lua所影响。
>> - 我并不认为追求简单就是在固步自封。我就是不喜欢上百M的编译器。
而且还非要标榜"简单", "优雅"。。。  听着就恶心。 C编译器上百M没有, C是不是不符合简单优雅了?

按相对路径加载, 不从正反两方面去想, 因为lua不支持, 而且目前也够好了, 于是就把它的坏处列完。 lua的屁股就这么香?



反之, "不无意间" -> "禁止", "不对正统绝对服从" -> "藐视", "提了一个诸多现代语言都支持的repl" -> "嘲笑"。。。
哦, 还有前面的"类型" -> "上下文" 。。。 这种游戏很好玩?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
88 [报告]
发表于 2012-11-18 21:10 |只看该作者
starwing83 发表于 2012-11-18 18:39
麻烦你看明白行吗?Lua的模块载入是上下文无关的。是独立的。意思是这样的:

require "..foo"

这一行语句本身,并不会额外带其他的信息——这只是普通的语句而已。因此,如果写着这一行语句的文件——可能是个普通lua文件,可能是个zip包,可能是个mpk文件(我们以前的私有打包格式),可能是在一个.c文件内部,甚至可能是一个独立的.o文件(LuaJIT支持)——不附带某些指向文件格式的信息,那么就根本无法推导..到底是什么。我们假设是个zip包吧。你想想看实现,require得从某个地方知道当前文件到底在哪儿,假设我很好心的给出了一个这样的路径:

zip://c:/data.zip/main.lua

那么也得有个东西能读取这个东西啊!现在的问题来了,如果require要从多个loader载入模块呢?那么这些loader就必须协商出一套能够在所有loader之间表述1.该文件是由哪个loader载入的;2.该如何查找他的相对路径模块文件。这种机制,除了官方指定,又怎么兼容?但是这种东西,明显是不会有任何语言会做的,那么实际上能做到的事情就只有两个了:1.不支持任意loader,只能由官方决定文件从几个有限的地方载入(如Java),这样你估计更有意见了;2.只支持文件路径相对载入,其他的就不管了,这样的后果是可能导致Bug(如果载入的几个地方都是?,持续往上找的结果可能是错误的)。这两者都是不完美的。所以Lua没有做任何限制——有本事,你自己做一套机制出来,反正材料都给你了。随便你。


不用相对路径的情况下, 多个loader就没问题? 就不需要协商这一套约定?
你说的多loader是多种类型的loader还是并发的loader?

包含 require "../foo"的文件是哪个? zip://c:/data.zip/main.lua?
加载zip://c:/data.zip/main.lua时遇见require "../foo" 怎么就不能得知这是想加载 c:/foo???呢?




starwing83 发表于 2012-11-18 18:39
但是,换一个角度思考这个问题——啊,我知道,你肯定又要选择性失明了,但是慢慢看,免得等会儿又无理取闹——从构架上来说,要求所有的库在一个目录下,要求这些库有清晰的相对关系,要求载入是明确的,这究竟有多么困难?就非要相对路径载入?就非要偷偷摸摸载入几个文件然后让谁也不知道?你说闭包能隐藏数据,可是那些数据是大白于天下的,是在文件里面的,是谁打开都能看得到的。数据的内容是隐藏的而其结构是完全看得见的。这难道不和公开载入模块完全相似么?(注意,模块的结构是完全公开的,但是模块内部干了什么事情是被隐藏的)

我知道你想说什么:“我就是有这种需求,你咋地?我就是不想遵循这个规则,你咋地?我就是想要相对载入文件我就认为这个好,你咋地?”那就直接没什么好谈了。语言是妥协的产物,Lua就是不这样,而你非要这样。我说了你能自己实现,你又嫌这种实现会有各种各样的问题,那我有什么办法?


只要数据内容隐藏了,私有/公有已经划分开了, 结构公开了又怎样? 你能访问? 不能。 于是之后的改变也不会影响你。
而目录结构公开就等于组织结构被公开。

我前面那个问题。 同样是私有/公有界面的划分, 域有闭包而模块什么都没有。 为什么前者就与lua不限制的哲学无关, 而后面就扯一堆哲学、思想、精髓?
约定俗成在我接受的范围内 —— 5个, 还要我再列一次吗? —— 老老实实的一开始就说"语言是妥协的产物","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
89 [报告]
发表于 2012-11-18 21:18 |只看该作者
回复 86# OwnWaterloo


    果然是这个………………这个的确叫reload,不过似乎叫“自动重载(auto reload)”我说的云风做的就是这个(不是Pluto神马的,你的dump把我误导了),最开始我说的就是这个。只是你说repl的结果最终可以persistent到文件这个说法把我误导了。

是的,我很想让Lua+Vim可以做到这样,需要做到这样的话,需要这样的工具:

1. Vim能向程序输出一个信号,表示某文件已改变。
2. Lua程序能收到这么一个(跨进程的)信号,得知文件已改变。
3. Lua能在不中断所有执行一致性的前提下,重新载入已改变的文件。

第一点不太难,不过得约定好输出的信号是啥。Vim自己内部本身是可以执行Lua程序的,所以将Vim本身作为host也不是不可以。但是Vim本身是个编辑器,编辑器有自己的事件循环,如果你的程序也有事件循环的话(基本上需要做自动重载的都有自己的事件循环——不然你也没办法重载啊),那基本上就是完蛋了。别的选择就不是很多了,socket,进程间通讯,消息队列,反正事儿不难,但也不太简单。

第一点解决,第二点就解决了。Vim保存文件并通知改变,Luahost的事件循环监听这个信号,并应用改变。

我第一次回的帖子实际上在说第三点。我的观点是,即使你用dofile,依然不能完全解决自动重载中遇到的问题。问题是这样的:

local string_gsub = string.gsub

for line in io.lines() do
string_gsub(line, ....)
end

这样的代码,函数是缓存的……

这样基本上你就完蛋了。

那么,能支持动态重载的模块,你必须保证,它的使用者,完全不会因为各种原因缓存模块里面的函数。因为Lua函数本身的灵活性,所以数据是无所谓的。

但是,如果数据格式上不兼容,为了不报错,恐怕数据重载也是必须的——这一点纯函数式的确是优势,但是恐怕也不明显。

dll的模块绝对不能用这种方式重载,一来是dll本身在注册表里面会留一个存根(用于退出的时候垃圾回收),这个存根得去掉,但是Lua没留办法,这倒不是说退出的时候会崩溃,这是说你根本无法改变这个dll——因为它会被Windows锁住。具体的方案得仔细研究Lua源代码才能得出,反正这不是很容易的事情。退一万步说,就算dll可以被重载,万一其userdat的格式不一样,那崩溃几乎是唯一结果了。

但是,就算只有这一点,保证也不是容易的:你如何保证,你的模块所有的使用者,都不会缓存这个模块呢?

最简单的办法,可能就是仅动态重载某些特定的模块了——我们以前的活动模块,导出函数只有OnDraw, OnInput等等几个,而且这些函数绝对不会被缓存(最初由同学设计的驱动循环是将这些函数逐一缓存的………………我@#¥%#%……@#……后来改过来了……)

通用的动态重载,在Lua里面是不可能的(或者至少是要靠运气的)。

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

emacs的shell里isatty是0。也有term, isatty就是1了。 只是Windows下只有shell。
不过repl其实不需要term。 要term也没什么用, 无非就是能用readline而已, 基于readline的也只适合补全补全git, darcs这种命令行工具。

也不一定非要shell。总之要的东西就是一个process, 一个够动态的语言。
shell(pipe)只是一种与之通信的方式。 其他通信方式也可以用, 比如socket。

编辑还是在vim里,爱怎么编辑怎么编辑。
然后需要执行一个方便的规则,能够很容易的选出需要被执行的代码。 lisp就是form。。。
最后只要vim能有办法和一个running process通信就可以了。比如vim里gdb是怎么搞的就怎么搞。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP