编程艺术之文本加工

最近在推上跟人吵编辑器什么什么的,加上最近看的几本书也都提到了文本处理的一些原则和技巧,这里把我的一些观点罗列出来,同时也作为自省的一个途径。

最近读到的书籍,主要就是《程序员修炼之道》(pragmatic programmer)和《Unix编程艺术》。另外值得提及的就是《C专家编程》。凡是讨论到这个话题,这些书都无一例外的表现出他们的黑客情结,即那种恪守清规的苦行僧般的程序大师。这种恪守清规的殉道精神纵然值得敬佩,但施用于工业则未免可笑。

编辑器的争吵已经经历了几十年,作为一个无名小辈我没有必要再跳进这个泥潭了。这里推荐的是《Unix编程艺术》中对5个Unix通用文本编辑器的讨论,虽然作者本人崇尚黑客精神,但讨论时及其严谨客观,对从精简的无可挑剔的神级编辑器ed到vi到复杂到无以附加的emacs等等,都做了及其客观的评价。并根据前文提出的十几条Unix程序设计原则以及一些程序复杂度的标准,对这些项目的设计目标和崎岖的发展历程做了总结。后文对多种编程语言的评论和分析,以及使用场合介绍,同样严谨客观,推荐诸位语言死忠阅读。

无论是语言,编辑器还是任何工具,都没有绝对的好与坏的争斗。必然在其使用范围和适用条件下有其可取之处。真正的程序员不应该抱定某种特定的环境不放,而应自如的运用任何一种工具,并将其威力发挥到极致。如同武林大师,任何武器都可手到擒来,随手抄起身边板砖菜刀,都能发挥出神入化的境界。

然而说到我本人的观点,仍难以克制心中对MS visual studio系列工具的鄙夷。首先MS这种封闭的文化确实难令人苟同,再加上他着力培养初级程序员,并通过繁杂的IDE环境来潜在的支配奴役程序员的做法更是让人感到仇恨。许多通过VB入门的人都感到自己算不上程序员,或者说难以学习其他程序。许多通过VC入门的程序员都感到VC和(不使用VC的)C++简直就是两门语言。我相信离开VS,大多数C#程序员都无法工作。我想这样的例子在编程论坛的新手区,可以找到极多例子。Apple是培养傻瓜用户,MS是培养傻瓜程序员。或许商业意义上他是对的。但作为程序员则绝无法忍受。

这也是我仇恨框架的理由。程序员不应被框架所奴役,而应随心所欲的使用程序库提供的便利。程序框架(Framework)给人的感觉就是,根本就是框架在编程序,而把你程序员当成是一个函数库。是他在调用你。作为一个程序员被程序戏耍的滋味绝不好受。这也是我对java没有好感的理由。一个程序要限定好他的界限范围,而这个要求对于一个框架来说太难做到了。一个大而全的东西总想做得更大更全,最后包罗万象,然后无可避免的走向毁灭。

最后谈一谈Unix编程思想中的“懒惰原则:宁多机器一分勿多程序员一秒”以及“生成原则:让代码生成代码”。这点在《程序员修炼之道》中也有不少论述。yacc和lex可能过于复杂了,但一些简单的脚本至少是很容易编写的。学习一门脚本语言,然后试着在C或者其他重型语言开发中使用脚本语言从旁辅助,生活会变得相当美好。在linux上cat+grep已经可以做很多事情。配合bash配合awk,或者任何其他脚本,比如流行的ruby或python,许多无意义的键盘敲击都能省却,许多依赖复杂IDE的需求,例如重构代码段,寻找函数调用和声明,检查编码规范等等,都可利用脚本完成。我之所以喜欢emacs一个重要原因就是自己写的这些脚本还可链接到一个键盘快捷键上,调用更加方便。

当然所有这些话题都是见仁见智了。或许有人会认为使用IDE正是符合了“宁多机器一分无多程序员一秒”的精神。假如确定这增加了的机器的一分钟确确实实能减少程序员一秒钟的开销的话,那也确实是值得的。

21 comments on “编程艺术之文本加工

  • OwnWaterloo says:

    如果只是为了C/C++(尤其是C++), VS+VA(再加ViEmu) 已经完美了……
    emacs神马的, 徒耗脑细胞……
    关键是…… 如此多的语言…… 越来越多的新语言……
    总不能见一个去学一个IDE吧……
    emacs之流, 完全是不得已而为之……

    • 唉。。关键是VS+VA都是收费软件啊。。。
      另外这俩家伙也是典型的资源怪兽。。。

      说到语言支持,其实MS也希望VS成为所有语言的IDE,可惜他那种封闭的小家子气基本上不可能做到那一点。J++就是个明显的失败案例。

      • OwnWaterloo says:

        收费又怎么了? 又不是天价。
        若工具真能提高效率, 投资一点钱算什么。
        时间啊, 青春啊, 生命啊才是最重要的。
        至少, 就我个人例子来说, 学了1年多的emacs, 编辑C++依然不如VS+VA+ViEmu给力……

        什么 xtags, xscope 都是fake。
        尤其是vim那个syntax, 脑袋吃屎了。
        居然仅仅为了语法高亮这么一个弱智功能去用正则。

        semantic算是走对了路。
        不过太慢, 而且对C++分析不够。
        当然, 还有一个我认为最靠谱的语法分析器 —— gccsense —— 没用过。
        不过相当大(比VA不知道大多少倍), 编译也相当耗时, 最后还编译失败了……

        gcc是干什么的? 它是跨平台的完整编译器, 能不大么?
        没办法的事, 就gccsense来说, 个人开发, 哪有精力去重写一个仅仅包含C++语法分析, 不包含代码生成的工具?

        软件开发是有成本的, 这种无盈利(至少盈利很少)的开发, 怎么去和VA比?
        毕业后我真打算要去买VA和ViEmu的……
        不过学了python之后, 确实忍受不了学门语言去找一堆工具来比较了……
        就抉择vim or emacs, 就走上了不归路……

        • 我其实是反对在编辑器里面加复杂的语法和语义分析器的。unix的理念是简单,做好一件事。简单的c分色显示是值得的,更复杂的功能,例如C++的类型分析,就没有必要。
          这也是C++之所以受人苛责的原因之一。每向C++添加一个类,其实就是增加了一道自定义的“心智模型”,一种新的“知识”,更别说复杂的重载继承关系。很多人甚至避免在C中使用过多typedef,就是防止过多的逻辑概念冗余。这不仅仅是拖累编辑器的分色显示而已,这其实就是设计上的缺陷,导致编程时候的纠结。从这个角度说,C++的类和继承关系,不仅仅没有减轻编程的复杂度,反而是大大增加了。这个复杂度,并不是通过定制编辑器或者IDE就可以解决的。

      • OwnWaterloo says:

        再说资源消耗。
        05+VA+ViEmu肯定比emacs+semantic+auto-complete要快, 非常明显。
        内存消耗大点算什么, 白菜价。
        auto-complete一顿一顿的让人烦。

        • auti-complete范围确定在文件内部就好多了,不要去做跨文件的语义分析,占资源,没必要,那种想把emacs变成vs的努力是方向性错误。
          我尽可能从设计的层面去解决问题,而不是借用编辑器的优越性。这也是前几次跟你讨论“交叉剪切粘贴”的原因。尽管利用编辑器的一些特性可能不成问题,但根本原因在于代码的重复之罪,如果设计合理,本不需要复杂的技术。

      • OwnWaterloo says:

        再说你对ms的鄙视。
        这是完全可以理解的, 我也鄙视……

        但鄙视归鄙视, 现实还是要认清:
        1. 有能力, 有需要去使用两位数以上的语言的人, 是少数。
        2. 只使用一门语言(甚至, 只适用一门语言中的某个部分)的人, 才是多数
        3. 太多低智力、 高体力消耗的编程工作, 得有人去完成
        4. 真正利他的人, 是少数; 利他的团体? 可能存在么?

        其实, 上面也说了, 像你这种geek, 其实是minority……
        与爱因斯坦相比, 所有人都是白痴, 他会这么想, 这么说么……
        适当的为majority想想吧……
        有那么多人帮你完成了那些低智高体的工作, 应该感谢才是……

        • 编程是艺术。说“majority”都是做低智高体的事情,其实那本身就源于MS对初级程序员的统治和压迫。原本应该用脚本,用正则或者代码生成器做的事情,变成了代码民工每天堆砌的砖墙。他们是我们应该解放出来的对象,而不是口上说“感谢”,心里却利用他们的劳动树立自己的“专家”地位。
          这也是我信奉“宁化机器一分,无花程序员一秒”的用意。是希望把程序员从机械的劳作中解脱出来,做真正有意义的事情。应该让现在的”minority”成为今后的”majority”,哪怕成绩很小,也该相信一步步的努力。

      • OwnWaterloo says:

        最后, 关于vim, emacs, 关于cli什么的。

        “搞不定就穷举”, 也是unix哲学里的吧。
        高效率的算法一般来说都是有更高的常数开销。
        对小规模的问题, 不一定就比低效算法好。
        而问题的规模通常很小。

        大致意思是如此吧?

        如上面所说, 大部分人, 编程的范围都很狭窄。
        而这些范围往往还有很好的, 呃可能还收费的软件支持。
        这些软件比vim, emacs什么的给力多了。

        而且很多人, 除了在ide上拖拖动动, 再copy, paste。
        没其他多少日常任务需要处理的。
        shell什么的, 这么多的unix命令什么的……
        确实, 如同vim,emacs一样, 是用得上, 绝对用得上, 但不值得花那么多时间去学习。
        编程不是生活的全部。

        你心目中的最优解, 不一定是所有人的最优解。
        C/C++执行效率快吧?
        “开发、学习效率慢, 我多花点时间不就是了” —— 这想法要对所有问题都能成功, 还有其他语言么?
        我在cu的functional版块还看见这样的说法:
        “我花5分钟用lisp完成功能, 并花20分钟执行”
        “比花2小时用C/C++完成功能, 并花5秒执行, 要来得快”
        他也不想想学lisp的时间……
        (时间上的)一次性投资, 也是投资, 也是成本。
        虽然随着时间的累积, 这种投资的效率会越来越高, 但别忘了, 时间这东西本来就很奇特。
        比如未来的时间可能不如当前的时间管用, 比如, 一个人的时间是有限的, 总会去的……
        用war3举个例,(不算dota……), 被动技能就是一次性投资, 不再使用魔法, 也没有冷却时间。
        但它们的收益, 通常还是不如主动技能。

        • 好吧。。
          归根结底写这篇文章我并不是想说服任何人。这只是我的自勉和自省罢了。
          对于不以编程为职业的人(也许搞IT,但是不亲自敲代码),自然没有必要花很多精力去追求最高效的开发技巧。
          并且我这里并不是推崇vim或者emacs多牛,我也提到我不希望“仅仅借助编辑器的优势”来做一些trick。当然一个舒适轻巧的编辑环境是我追求的目标,但我更加推崇的是从设计的简洁正交的层面,获得代码开发和阅读的清晰度。用“代码生成”,其实也是为了免除“重复之罪”。编辑器只是工具,不是目的。我也提到过其实牛人应该是无论什么样的编辑器都能得心应手的。我想,那原因一定是源于编程设计的清晰明了,简洁正交。

  • OwnWaterloo says:

    我也不是为了说服你…… 我自己还用emacs来着……
    看见一些人做的一些小工具, 为20行功能代码写200行gui, 也真觉得别扭……
    但是呢, 因为各种先天后天的原因, 人与人之间达成共识, 比拥有分歧要难太多。
    相互多些理解吧……

  • OwnWaterloo says:

    跑点题…… 说点编辑器之争关联不大的问题。

    我感觉崇尚unix的许多人, 都有一个误区:
    unix可以高效完成任何事, 若某事不能高效完成, 那这事就是没用的。
    再我看来这完全是借口……

    你说的改善设计是一码事; 但即使对设计得很好的代码, 那些功能我感觉依然是有用的。
    比如某api(假设设计得足够好), 一下子回忆不起参数个数或者顺序什么的;
    某结构体, 一下子忘了某成员的确切拼写;
    难道去man? 这有效率吗?

    你是不是又要说”程序员应该对它使用的api很熟悉”?
    或者多man几次就会加深记忆?
    我感觉又是借口。
    常用的库(比如标准库) 能记住确实是好事。
    对那些不太常用的, 只要能提示参数列表就肯定能用对的, 但时间长了忘了的情况; 若机器能协助记忆, 也算是帮助。

    编程的一个目的不就是发挥机器的长处: 机械重复、 记忆的准确与容量么?

  • OwnWaterloo says:

    “每次只做一件事, 但做到最好” —— 这也确实是很好的准则。
    但上面提到的 tags 这样的fake工具s, 没一样是做好了的。

    什么叫一件事?
    这个原则有一个很重要的前提: 足够正交化。
    语言的语法分析本来是一件事, 却被拆散到各个部分去完成。
    自然每部分都没法完成好。
    这就是那些工具的问题。

  • OwnWaterloo says:

    关于majority, 编程艺术什么的……

    编程是艺术的年代早就过了……
    你感觉的”解放”, 对于他们来说并不一定是好事。
    有部分人 —— 你肯定见过不少这样的人 —— 能把程序写得不出错, 就已经谢天谢地了。
    让他们好好研究自己所使用的那种技术吧…… 多几门技艺对他们来说真的无法承受的负担。

    不是人人都为了艺术去编程的, 为了混口饭吃的人更多。

    我说感谢, 并没有什么自诩专家的意思。
    熟练几个工具而已, 说专家, 好意思么?
    社会分工不同而已。

    编程是很艺术, 很愉快的。但不可能让所有人去做这种艺术且愉快的工作。
    最基本的, 得有饭吃, 有衣穿, 有房住。
    我不会种地, 缝衣, 筑房; 所以我感谢提供这些东西的人 —— 无论怎样, 替我做了我不能或不愿做的事。
    如果有人愿意入编程这行, 我也乐意领路 —— 对程序员来说, 分享也是一件乐事。
    但上面也提到了, 有些时候他人不愿, 有些时候他人不能。
    从社会需要以及个人条件来说, 社会分工是必然的结果。

    上面是整个社会; 更上面还提到编程早就不艺术了。
    编程如今也是分工很细的活动 —— 编程的需求量和从前早就没得比了 —— 自然会有一些(在我看来)不那么艺术和愉快的工作。
    如同社会分工, 在编程中的细致分工中我也感谢那些做了我不能或不愿做的事的人。

    • 我一直认为之所以这样是教育的问题。一开始的教育就建立在快速培养技术工人的基础之上,自然教育出大批这样的程序员。
      那些用“教育”骗饭吃的砖家叫兽当然不希望教出的学员一个个文武精通。公司给员工的培训当然不希望员工学会了就跳槽。因此个人技艺只有通过自己的修炼。我想你我之于那些“初级程序员”其实并不见得更聪明,只是个人修炼得更提前些读书更多些。所以即使是他们也是我们可以解放的对象。就像黑客帝国里面解救母体里受困的人类一样。只是方式方法上可能需要技巧。。哈哈,这又是另一个话题啦,以后有机会单独写博客再说这个吧|||

      • OwnWaterloo says:

        终于还是谈到这个最底层的原因了……
        “勿谈国是”……
        有段时间我也挺粪的…… 但想想自己也改变不了什么, 那也只好凑合了……
        听到过一个说法, “工程师就是要在有限的条件下解决问题”。
        投胎没选esay模式也好, 不然多没挑战性……

  • OwnWaterloo says:

    哎…… 这些有的没的一说废话一大堆……
    还是讨论技术比较赏心悦目……

    所以还是来讨论技术吧……
    就那个 vim, syntax, tags, scope, flymake什么的问题……

    我设想的一种方式是这样: 有一个功能完整的, 真实的(而不是什么正则表达式来”凑合”)语法分析器。
    一直在后端运行。
    代码高亮、 完成、 跳转、 搜索、 flymake什么的, 都从背后的语法分析器获取结果; 而不是各自为政, 为了一个单独的目的去实现一个简陋的东西 —— 语法分析本来就是一个严谨、 独立且完整的工具。
    所以我才说上面那些fakes工具没一样是做好了的, 为了个顾个的, 不得不自己实现一个简陋的”语法猜测”器。

    这样, 编辑代码的同时就是在产生一颗语法树; 而不是为了highlight去分析一遍, 为了complete去分析一遍、 为了browse去分析一遍, 为了compile最后再去分析一遍。
    整个编辑中, 都可以获得编译器的完整帮助; 交互的间隔非常短, 而不是仅仅在 gcc -c时。
    甚至还可以和gcc后端或者llvm结合, 省去大量的编译时间。

    • 哈哈跟你讨论真给力!
      回去我把这些论点整理一下另外发一篇博客吧,顺便继续讨论这个话题。
      vim我用的不多,我发邮件问问看@rainux和@pipitu吧。
      emacs下的semantic很给力,不过也存在效率不高的问题。

      • OwnWaterloo says:

        vim我也不熟…… 但我觉得vim用熟了, 应该会比emacs给力……
        至少git的editor我还是用的vim……

        当初比较emacs和vim, 最后完全是在比较elisp和viml……
        感觉Imperative语言已经学得很多了, 对viml完全不感兴趣……
        于是就选了elisp

        现在感觉很失望啊。
        就functional来说…… 还不如C++ template metaprogramming 得到的体会多……
        也不知道它怎么被归类到functional的……
        还有其他很多方面都体会不到美感, 又是一门凑数的语言……
        虽然通过elisp还是体会到了一些别的语言没有的东西, 但总体来说与希望相差太远。
        不过也没法回头了……

      • OwnWaterloo says:

        嗯, 架构和semantic差不多, 都是以分析器暴露出获取结果的接口, 其他应用以之为核心。
        但分析的方式可能会有点区别。
        普通的编译器, 是在一坨死的文本上分析, “该文本是否合乎语法?”
        semantic也是走的这条老路, lex, syntax, semantic…
        似乎是用一个timer(或者还有其他机制)来触发对code的另一次完整分析。

        我设想的方式是从已有的正确的code上不断添加/展开。
        毕竟这不是专用编译器, 而是为编辑器设计的分析器, 可以获得比编译器多得多的交互机会。

        比如, 现有的code已经是一颗完整的, 正确的语法树, 当写下:
        struct 时, 会展开为:
        struct { };
        此时新加入的节点的语法(按C来说)是不正确的, 只作为一个不完整节点, 不会添加到最终的语法树当中。

        分析器会暴露出获取这种不完整节点的接口, 编辑器可以作一些处理, 比如在{}之间加上下划线, 使之更可视化一点。
        编辑器还可以从分析器那里获取不完整节点需要补充什么的信息, 比如这里需要添加至少一个成员。

        成员首先要说明类型, 编辑器可以从分析器那里获取”这里可以展开出一些什么类型”。
        比如, 从 some_ 为前缀, 搜索出 some_type , 然后展开为:
        some_type ; 并继续提示缺少成员名。

        直到输入 x; 这个不完整节点 struct { }; 才扩展为一个完整节点 struct { some_type x; };
        并提交到语法树中。

        然后可以继续添加一些可选的部分, 比如 struct { some_type x; other_type ; };
        other_type x; 作为一个子节点, 又是不完整的了。

        对那种大幅度的修改, 比如 some_type 的定义被删除了, 如何影响到上述struct, 可能会很复杂。
        比如上述节点会在some_type中的依赖列表中添加一项, 当some_type被删除时, 所有依赖项都被重新设置为不完整节点。
        这只能具体实现时再说了。
        毕竟, 编译原理那点东西, 考完就忘光了…… 教育啊……
        如果不行, 或者效率不够好, 可能还是要走老路……

      • OwnWaterloo says:

        再说说semantic …… 这东西现在已经默认被我关了……
        感觉对C++分析不够完整(具体哪忘了, 好像重载都不能区分)。
        我自己也暂时没想要去分析C++ …… 太,太tm复杂了……
        而对C, 还真像你说的, 语言简单, 不用也罢…… 吐出的回报与吃的资源相比感觉不值得……

        总是不断听到所谓的”效率次要”什么的。 感觉也不能一概而论。
        就正在editing来说, 有一点延迟就会让人很难受……

Comments are closed.