Ruby VS Python VS JS

对比三种动态语言

语言定义

首先看一下 wikipedia 上对这三门语言的定义:

  • Python is a widely used high-level, general-purpose, interpreted, dynamic programming language
  • Ruby is a dynamic, reflective, object-oriented, general-purpose programming language.
  • JavaScript is a high-level, dynamic, untyped, and interpreted programming language.

其实上面的关键字对于这三门语言来说都适用,只是每个语言的强调点不一样而已。

这三门语言都是动态类型语言,支持函数式、面向对象两种编程范式。

语言特性

当然我在扯淡里提到,任何一种“语言”,都是各种“语言特性”的组合。

这些语言特性,就像你在选择一台电脑的时候,看它里面是什么配置。选电脑的时候,没有人会说 Dell 一定是最好的,他们只会说这个型号里面装的是 Intel 的 i7 处理器,这个比 i5 的好,DDR3 的内存 比 DDR2 的快这么多,SSD 比磁盘快很多,ATI 的显卡是垃圾……

如此等等。这些配置对应到程序语言里面,就是所谓“语言特性”。举一些语言特性的例子:

  • 变量定义
  • 函数定义、函数调用
  • lambda 函数
  • 面向对象
  • 垃圾回收

基本语法

变量定义

但是到了Ruby中,各种符号,像class Son < Father表示类的基础,”hello” << “ world”表示字符串的拼接,@var表示对象的成员变量,@@var表示类的成员变量,$var表示全局变量。

变态

变量类型

动态语言最主要的特点就是变量无类型,利用反射机制可以查看运行时变量的值的类型。

1
2
3
4
$ node
> str = "hello world"
> typeof str
'string'
1
2
3
4
$ irb
> str = "hello world"
> str.class
String
1
2
3
4
$ python
> str = "hello world"
> type(str)
<type 'str'>

字符串

不可变

字符串的拼接

1
2
3
4
5
6
$ node
> var a = 5;
> var b = 10;
> console.log(`Fifteen is ${a + b} and\nnot ${2 * a + b}.`);
// "Fifteen is 15 and
// not 20."
1
2
3
4
5
$ python
> long_string = """
> my name is {username},
> my age is {age}
> """.format(username="zhangsan", age=10)
1
2
3
4
5
$ irb
> long_string = """
> my name is %{username},
> my age is %{age}
> """ % {username: "zhangsan", age:10}

函数调用

在 ruby 中,方法调用时的括号可有可无,即使有参数也可以省略:

1
2
3
4
5
6
> def add(a, b)
> a + b
> end
>
> add 1, 2
=> 3

lambda 表达式

lambda 表达式表示的是匿名函数,也就是我们通常说的闭包。由于在这三门语言中,函数均是一等成员,所以可以很方便的进行函数式编程

1
2
3
$ node
> [1,2,3].map((x) => x + 1)
[ 2, 3, 4 ]
1
2
3
$ python
>>> map(lambda x: x+1, [1,2,3])
[2, 3, 4]
1
2
3
$ irb
> [1,2,3].map &(lambda {|x| x+1})
=> [2, 3, 4]

Python 的 lambda 表达式是这三者中最弱的一个,只能包含一个表达式,javascript 与 ruby 的则没有这种限制。

ruby 版本的 lambda 前有个&,这是因为在 ruby 中,方法除了接受参数外,还可以接受一个代码块 (block),代码块在 ruby 中有两种写法:

  • 一行的话用{}
  • 多行的话用do … end
1
2
3
4
5
6
7
8
> [1,2,3].each { |num| print "#{num}! " }
1! 2! 3!
=>[1,2,3]
> [1,2,3].each do |num|
> print "#{num}!"
> end
1! 2! 3!
=>[1,2,3]

& 的作用是告诉解释器,现在传入的不是正常的参数,而是一个代码块。

面向对象

面向对象主要的核心是用对象来达到数据封装的目的。

javascript 基于原型链实现面向对象。

python、ruby 基于类来实现面向对象,和 java 类似,但是更纯粹些。

1
2
3
4
5
6
7
8
9
10
11
$ python
>>> def func(): return 1
>>> type(func)
<type 'function'>
>>> func2 = lambda x: x
>>> type(func2)
<type 'function'>
>>> type(1)
<type 'int'>
>>> dir(1)
['__abs__', '__add__', .....]
1
2
3
4
5
6
7
8
9
$ irb
> def add(a, b)
> a + b
> end
> method(:add)
=> #<Method: Object#add>
# 上面 ruby 的例子中,使用了 Symbol 来表示 add 方法,这是由于 ruby 中直接写 add 表示函数调用
> 1.methods
=> [:%, :&, :*, :+, :-, :/, .....]

使用感受

  • Python,一件事用一种方式实现

设计思想,“There should be one– and preferably only one –obvious way to do it.”

举个例子,Python里,只要数据结构是sequence,获取长度就是len(tuple/list/array)

  • Ruby, 一件事用多种方式实现

设计思想,“There are many ways to do it.”

举个例子,Ruby里取数组长度,length,还可以用size,size是length的别名

再比如,
点唱机可能希望记录每首歌被播放的次数。这个数目可能是Song对象的一个实例变量。当一首歌被播放时,实例中的值增加。但是,假如我们还想要了解下一共播放了多少首歌。通过搜索所有Song对象并累加它们的播放次数,或者可以直接使用全局变量来完成统计;再或者,让我们使用类变量。

再举个例子,
当你想要访问类的方法时,首先需要实例化类。然后,使用对象可以访问类的任何成员。
同时Ruby 提供了一种不用实例化即可访问方法的方式。让我们看看如何声明并访问类方法:

1
2
3
4
5
6
class Accounts
def reading_charge
end
def Accounts.return_date
end
end

我们已经知道方法 return_date 是如何声明的。它是通过在类名后跟着一个点号,点号后跟着方法名来声明的。可以直接访问类方法,如下所示:

1
Accounts.return_date

如需访问该方法,就不需要创建类 Accounts 的对象了。

以上

参考阅读