• 为什么 Ruby 是一种受欢迎的 LISP


    译言网 | 为什么 Ruby 是一种受欢迎的 LISP

     

    为什么 Ruby 是一种受欢迎的 LISP

    于2007-08-13 04:55:38翻译 | 已有7441人浏览 | 有4人评论

    几年之前,我考虑过 Ruby 并决定忽视它。Ruby 不像 Python 那么流行,也不像 LISP 那么强大。为什么我要把时间花在这上面? 当然,我们可以反过来考虑这个事情。如果说 Ruby 比 LISP 更流行,比 Python 更强大会怎样?这样就足够使 Ruby 变得有趣了? 在回答这个问题之前,我们应该确定是什么使得 LISP 如此强大。Paul Graham 写过一些颇有说服力的文章来说明 LISP 的优点。出于讨论的目的,我把这些文章的内容归纳为两点: *LISP 是一个紧凑的函数式语言。 *LISP 拥有可编程的宏。 在下面会看到,Ruby 也堪称一个函数式语言,它对宏的模拟比我想得还要好。 Ruby 是一个比 LISP 更紧凑的函数式语言 一门紧凑的语言可以让你简洁地,没有疑惑地把意思说出来。你一眼可以看到.....

    几年之前,我考虑过 Ruby 并决定忽视它。Ruby 不像 Python 那么流行,也不像 LISP 那么强大。为什么我要把时间花在这上面?

    当然,我们可以反过来考虑这个事情。如果说 Ruby 比 LISP 更流行,比 Python 更强大会怎样?这样就足够使 Ruby 变得有趣了?

    在回答这个问题之前,我们应该确定是什么使得 LISP 如此强大。Paul Graham 写过一些颇有说服力的文章来说明 LISP 的优点。出于讨论的目的,我把这些文章的内容归纳为两点:

        LISP 是一个紧凑的函数式语言。
        LISP 拥有可编程的宏。

    在下面会看到,Ruby 也堪称一个函数式语言,它对宏的模拟比我想得还要好。
    Ruby 是一个比 LISP 更紧凑的函数式语言

    一门紧凑的语言可以让你简洁地,没有疑惑地把意思说出来。你一眼可以看到更多的程序,没有更多的地方可供 bug 藏身。到了一定程度之后,能够使你的程序更紧凑的唯一方法就是使用更有力的抽象。

    一个特别的、强大的抽象是 lambda 。借助 lambda ,你可以即刻建立一个新的函数,并将它传给其它函数,甚至保存起来留待以后再用。比如, 如果你想将表中的每个数字都扩大一倍,你可以写成:

    (mapcar (lambda (n) (* n 2)) mylist)

    mapcar 通过变换 mylist 中的每个元素建立了一个新的列表。在这个例子中,这个变换可以被读作“对每一个值 n ,乘以 2 。”用 JavaScript 中的 function 来代替 lambda ,可能会清楚一点:

    map(function (n) { return n * 2 }, mylist)

    当然,这只是关于你能用 lambda 做什么的一点提示。偏爱使用这种风格编程的语言被称作函数式语言,因为它们利用函数进行工作。一旦你学着阅读由一种紧凑的函数式语言所写的程序,你会发现它真的非常简洁,非常清晰。

    Ruby 在函数式程序设计上如何与 Lisp 一较高下呢?让我们考虑 Paul Graham 的一个经典例子,一个函数,建立一个累加器:

    (defun foo (n) (lambda (i) (incf n i)))

    这段代码用 Ruby 来写的话会更短一点,而且写法对于 C 的用户来说更熟悉一点:

    def foo(n) lambda {|i| n+=i} end

    acc = foo 3
    acc.call(1)    # --> 4
    acc.call(10)   # --> 14
    acc.call(0)    # --> 14

    但是在 Ruby 中有一种有趣的特殊情况可以让我们打更少的字。考虑一个(非常傻的)函数,它需要一个 lambda 作为参数:

    $ Call 'fn' once for each natural number.
    (defun each-natural-number (fn)
      (loop for n from 1 do (funcall fn n)))

    $ Print 1, 2, 3...
    (each-natural-number
     (lambda (n) (format t "~D~%" n)))

    现在,我们可以用 Ruby 写相同的函数:

    def each_natural_number(fn)
      n = 0
      loop { fn.call(n += 1) }
    end

    each_natural_number(lambda {|n| puts n })

    但是我们可以做到更好。让我们用 yield 替换掉 lambda 和 fn :

    def each_natural_number
      n = 0
      loop { yield n += 1 }
    end

    each_natural_number {|n| puts n }

    是的,yield 是一个有特定用途的技巧,并且只在那些需要单独一个 lambda 的函数中工作。但是在高度函数化的代码里面,yield 对我们非常有帮助。比较:

    [1,2,3].map {|n| n*n }.reject {|n| n%3==1 }

    (remove-if (lambda (n) (= (mod n 3) 1))
               (mapcar (lambda (n) (* n n))
                       '(1 2 3)))

    在一个大型程序中,这就有区别了。(LISP 一方的辩解是,可以写一个 reader 宏,让 lambda 变得更简洁。但是很少有这么做的。)
    你想从宏中得到的东西,Ruby 能给你差不多 80%

    关于这一点,LISP 用户会说:“lambda 的语法很棒,但是宏又怎么样呢?”这是一个好问题。LISP 宏是一种函数并且:

        由编译器运行
        将自定义的语法转换成原始的 LISP 。

    宏最常见的用途是避免键入太多的 lambda :

    (defmacro with-each-natural-number (n expr)
      `(each-natural-number (lambda (,n) ,expr)))

    (with-each-natural-number n
      (format t "~D~%" n))

    defmacro 定义了一个函数,函数需要一个列表作为参数,并返回另一个列表。在这个例子中,每一次编译器看到 with-each-natural-number 时都会调用我们的宏。它使用了 LISP 的“反引号”语法从一个模板中快速构造出一个列表,并替换掉 n 和 expr 。然后列表被传回编译器。

    当然,这个宏在 Ruby 中没什么用,因为 Ruby 中不存在这个宏所需要解决的问题。

    宏的第二个最常见的用途是通过建立迷你语言来定义东西:

    $ Generate some bindings to our database
    $ using a hypothetical "LISP on Rails."
    (defmodel <order> ()
      (belongs-to <customer>)
      (has-many <item> :dependent? t))

    使用 Ruby on Rails ,我们可以这么写:

    class Order < ActiveRecord::Base
      belongs_to :customer
      has_many :items, :dependent => true
    end

    这里,belongs_to 是一个类方法。调用时,会将一批成员方法添加到 Order 中。方法的实现相当难看,但是接口确实漂亮。

    对任何类似宏的功能的真正考验是,是否经常建立迷你语言。这方面 Ruby 取得了良好的成绩:除了 Rails ,还有 Rake(用来编写 Makefiles ),Needle(连接组件),OptionParser(解析命令行选项),DL(与 C APIs 交互)等等,不计其数。Ruby 程序员使用 Ruby 编写任何东西。

    当然,有许多高级的 LISP 宏不能被简单地移植到 Ruby 中。特别是,那些编译迷你语言的宏还没有出现,虽然有足够的工作使它们有可能出现。( Ryan Davis 在这个方向上已经完成了一些有前途的工作,ParseTree 和 RubyInline ,我会写一些相关技术的文章。)
    Ruby 有优秀的库、社区,成长势头良好

    那么如果 LISP 仍然比 Ruby 更强大,为什么不使用 LISP ?反对使用 LISP 编程的常见理由有:

        没有足够的库。
        我们没法雇佣 LISP 程序员。
        在过去 20 年里看不到 LISP 的影子。

    这些并非是压倒性的理由,但是它们确实值得考虑。

    曾几何时,人们认为 Common Lisp 标准库太巨大了。但是今天,人们又苦于它太小了。Java 的手册能够堆满一面墙,Perl 的 CPAN 存档有满足你能想象到的任何目的的模块。相比之下,Common Lisp 甚至没有标准的方法进行网络通信。

    同样地,LISP 程序员太少了。如果你在波士顿附近,这有一小群可以创造神奇的头发斑白的黑客。在其它地方,那些好学的年轻黑客又非常分散。LISP 总是一种少数派语言。

    另一方面,Ruby 正在流行中快速地成长。最大的动力似乎是 2004 年末开始的 Rails ,如果你试着开一家公司,那么每一个可能的员工都是 Rails 的粉丝已经是司空见惯的事情了。Rails 很快将纳入到一般的 web 开发中,并最终从其中产生庞大的商业机会。

    Ruby 用足够久的时间开发了一个优秀的标准库,大量的附加库。如果你需要下载一个网页,解析 RSS ,生成图形或者调用 SOAP API ,你所要的都已经做好了。

    现在,在一门强大的语言和一门流行的语言中作选择的话,选择强大的那个可能更好。但是如果强大的功能是次要的,那么流行的语言就具备了所有的优点。在 2005 年,要弃 Ruby 选 LISP ,我会很艰难地考虑很长一段时间。可能我只会在需要优化代码,或者需要宏来扮演一个全功能的编译器时才会这么做。

    (感谢 Michael Fromberger 审阅了本文早期的一个草稿。)

  • 相关阅读:
    org.apache.xerces.dom.ElementNSImpl.setUserData(Ljava/lang/String;Ljava/lang
    case when then 中判断null的方法
    Oracle 傻瓜式数据归档
    Object type TYPE failed to create with error
    导出表结构到Excel 生成代码用
    Intellij 高亮显示与选中字符串相同的内容
    自定义命令杀死 java 进程 alias kjava
    R语言包_dplyr_1
    dplyr包
    在天河二号上对比Julia,Python和R语言
  • 原文地址:https://www.cnblogs.com/lexus/p/2646990.html
Copyright © 2020-2023  润新知