最近一直在看红宝石(ruby)语言,到现在为止,算是对其设计有一些了解。作为一动态语言,ruby 经常会拿来与 python 对比,确实这两门语言在语法层面、实现层面有很多共同的地方,但是它们也在很多设计理念上存在重要差异,通过对比这些相同点、异同点,更加有助于理解这两门语言。同时,Node.js、React Native 的出现,将 javascript 这门“前端”语言推向了全栈,同样作为一门动态语言,javascript 与 ruby、python 在很多概念上也存在很多相同点、异同点。
这篇文章将尝试介绍这三门语言的一些特点与不同点,让我们开始吧。
既轻量又强大
是大多数动态语言相通的设计理念,关于 javascript 设计理念更多的介绍可以参考我的这篇文章介绍。至于 Python 与 Ruby 设计理念的区别,一句话即可概括:
比如,Python 中 Tuple, Array, String 没有相应获取大小的方法,而是提供了统一的 len
来解决这个问题
>>>len([1,2]) 2 >>> len("hello world") 11 >>> len((1,2)) 2
至于 Ruby 的 一件事情有多种方法做
的理念,后面我在讲解 lambda 时再介绍。
如果你之前没接触过 ruby、python 的语法,推荐先去了解下:
javascript 实在是太简单了,就不用特别看了。
综合来说,python、javascript 还是比较中规中矩的,即使 ES6 里面加了 很多花哨的语法糖衣 ,但是也比较直观,但是 ruby 这个语言就比较变态了,各种符号,像 class Son < Father
表示类的基础, "hello" << " world"
表示字符串的拼接, @var
表示对象的成员变量, @@var
表示类的成员变量, $var
表示全局变量。
而且在 ruby 中,方法调用时的括号可有可无,即使有参数也可以省略:
> def add(a, b) > a + b > end > > add 1,2 =>3
如果你对 Scheme 熟悉,上面的代码还能像下面这么写,是不是很亲切
> (def add a, b > a + b > end) > (add 1,2) =>3
这也就是充分说明,括号在 ruby 中只是起到了“分割”的作用,并没有什么语法含义。
面向对象主要的核心是用 对象
来达到数据封装的目的。
$ python >>> deffunc():return1 >>>type(func) <type 'function'> >>>func2 = lambdax:x >>>type(func2) <type 'function'> >>>type(1) <type 'int'> >>>dir(1) ['__abs__', '__add__', .....] #--------------------------------------------------# $ irb > defadd(a, b) > a + b > end > method(:add) =>#<Method: Object#add> # 上面 ruby 的例子中,使用了 Symbol 来表示 add 方法,这是由于 ruby 中直接写 add 表示函数调用 > 1.methods =>[:%,:&,:*,:+,:-,:/, .....]
可以看到,在 python、ruby 中,像 1
这样的数字字面量也是对象。
lambda 表达式表示的是匿名函数,也就是我们通常说的闭包。由于在这三门语言中,函数均是一等成员,所以可以很方便的进行函数式编程
$ node > [1,2,3].map((x) => x +1) [ 2, 3, 4 ] #--------------------------------------------------# $ python >>> map(lambda x:x+1, [1,2,3]) [2, 3, 4] #--------------------------------------------------# $ irb > [1,2,3].map &(lambda {|x| x+1}) =>[2,3,4]
Python 的 lambda 表达式是这三者中最弱的一个,只能包含一个表达式,javascript 与 ruby 的则没有这种限制。
细心的读者会发现上面 ruby 版本的 lambda 前有个 &
,这是必须的,否则会报下面的错误
ArgumentError: wrong numberofarguments (given1, expected0)
这是因为在 ruby 中,方法除了接受参数外,还可以接受一个代码块(block),代码块在 ruby 中有两种写法:
{}
do ... end
> [1,2,3].each { |num|print"#{num}! "} 1!2!3! =>[1,2,3] > [1,2,3].eachdo|num| > print"#{num}!" > end 1!2!3! =>[1,2,3]# Identical to the first case.
&
的作用就相当于告诉解释权,我们现在传入的不是正常的参数,而是一个代码块。这个传入的代码块在方法内通过 yield
进行调用。我们可以自己实现个 my_each
classArray defmy_each i = 0 whilei <self.size yield(self[i]) i+=1 end self end end > [1,2,3].my_each { |num| print"#{num}!"} 1!2!3! =>[1,2,3]
ruby 中 lambda 表达式属于 Proc 类型 ,
> lambda {|x| x}.class =>Proc
更多可以参考:
就像上面说的,ruby 中 yield
就是表示代码块的调用,没有其他含义。而在 python 与 javascript yield
是用来构造生成器(generators)的,只不过两门语言的语法略有区别,都是用来控制程序的运行流程,相当于用户态的“进程”:
$ python defiter(): forxinxrange(10): yieldx foo = iter() printnext(foo) printnext(foo) #--------------------------------------------------# $ node.js function* iter() { for(var i =0; i <10; i++) yieldi } var foo = iter() console.log(foo.next().value) console.log(foo.next().value)
上面两份代码都依次打印出 1
, 2
。
关于生成器的更多资料,可以参考:
在 ruby 中,与生成器对应的概念是 Fiber ,例如:
iter = Fiber.newdo (0..10).eachdo|x| Fiber.yield x end end puts iter.resume puts iter.resume
上面的代码也依次打印出 1
, 2
。
关于生成器与 Fiber 的关系,可以参考:
node_modules
文件夹内 经过上面简短的介绍,我相信大家对这三门语言有了全面的理解,多了解一门语言,也就是多个解决问题的思路。
个人感觉,Python、Javascript 的语法比较中规中矩,适合大部分程序员学习。Ruby 更适合 geek 去学,因为它的很多奇特语法会让你思考语言的设计细节,而不仅仅是使用这么简单,最近我在看 Ruby元编程 ,里面的很多内容就很有意思,一些内容在看 SICP 时就已经遇到,这种感觉很棒,我相信对编程语言的了解又加深了一步。谢谢 Yukihiro Matsumoto 带给我们 ruby 这么美妙的语言。