分享

从其他程式语言到Ruby

 思考的轨迹 2012-02-21

当你第一眼看见Ruby 程式码,你一定会想起你熟悉的程式语言。这正是本文件的目的。Ruby 有许多语法和Perl、Python 和Java (以及其他程式语言) 类似,所以如果你已经熟悉这些程式语言,那么学习Ruby 易如反掌。

这份文件包括两大部份。这一部分的用意是整理从X语言到Ruby的 ??重点。第二部分则从Ruby的 ??重要功能及特色着手,与其他程式语言来做比较。

重点整理:从程式语言X到Ruby

重要的语言特色及一些诀窍

这里是你学习Ruby 的一些重点及提示。

迭代(Iteration)

Ruby有两个常用的特色你可能没见过,那就是“程式区块(blocks)”和迭代子(iterators)”。不像使用索引的回圈(例如C, C++和pre-1.5 Java) ,或是回圈控制结构(例如Perl的for (@a) {...},或是Python的for i in aList: ... )。在Ruby里你会常常看到:

some_list . each  do  | this_item | 
  #我们在程式区块中
  #处理this_item
end

关于更多each的资讯(以及collect , find , inject , sort等等),请参考ri Enumerable (和ri Enumerable# func_name ).

一切东西都有值

表达式(expression)和叙述(statement)没有差别,都会有回传值,即使那个值是nil例如下述用法:

x  =  10 
y = 11
z = if x < y
  true
else
  false
end
z # => true

Symbols 不是轻量化的字串

许多Ruby 新手会搞不清楚什么是Symbols(符号) 可以做什么用。

Symbols就如同一个识别符号。一个symbol就代表它是”谁”了,而不是代表它是”什么”。打开irb来看一看它们的区别:

irb ( main ): 001 : 0 >  :george . object_id  ==  :george . object_id 
=> true
irb ( main ): 002 : 0 > " george ". object_id == " george ". object_id
=> false
irb ( main ) : 003 : 0 >
object_id方法会回传物件的识别编号。如果有两个物件有相同的 object_id表示它们其实是同一个(指向同一个记忆体位置)。

如你所见,使用过Symbols之后,任何相同名字的Symbol都是指记忆体里的同一个物件。对任何相同名字的Symbols,它们的object_id都一样。

让我们来看看字串“george”,它们的object_id并不相同。这表示它们在记忆体里面是不同的物件。每次你建立一个新的字串,Ruby就会分配新的记忆体空间给它。

如果你不清楚何时使用Symbol 何时用字串(String),想想看用途究竟是物件的识别(例如一个杂凑Hash 的key),还是物件内容(比如这个例子的“george”)。

所有东西都是物件

“所有东西都是物件” 并不是夸大,甚至是类别跟整数也是物件,你可以与其他物件一样操作它们:

#这是等价的程式:
# class MyClass
# attr_accessor :instance_var
# end
MyClass = Class . new do
  attr_accessor :instance_var
end

译注:在Ruby中任何类别都是由Class类别所实例(new)出来的物件。

可变的常数

常数(Constant)并不真的无法改变。如果你修改了一个已经有值的常数,你会得到一个警告讯息,但程式不会终止。当然这不表示你”应该”修改常数的值。

命名惯例

Ruby规定了一些命名惯例。变数的识别名称,大写字母开头的是常数、 ??钱号($)开头的是全域变数、@开头是实例变数(instance variable)、@@开头则是类别变数。

方法名称可以允许大写字母开头,虽然可能造成一些混淆,例如:

Constant  =  10 
def Constant
  11
end

这里的Constant是10,但是Constant()却是11。

虚拟关键字参数

Ruby 不像Python 有关键字参数(keyword parameters)功能,但是可以用symbols 和杂凑(hash) 来替代。Ruby on Rails 和非常多的函式库都使用了这个方法,例如:

def some_keyword_params (  params  ) 
  params
end
some_keyword_params ( :param_one => 10 , :param_two => 42 )
# => {:param_one=>10, :param_two=>42}

一切为true

在Ruby里,除了nilfalse之外的所有东西,都可以当做true值。在C, Python和其他语言中,0和一些其他值,例如空列表,会被当做false。例如我们看看以下的Python程式(其他语言亦同):

# in Python 
if 0 :
  print " 0 is true "
else :
  print " 0 is false "

这会输出“0 is false”。而在Ruby 里:

# in Ruby 
if 0
  puts " 0 is true "
else
  puts " 0 is false "
end

这会输出“0 is true”。

存取修饰词会作用到底

在下面的Ruby 程式中,

class MyClass 
  private
  def a_method ; true ; end
  def another_method ; false ; end
end

你可能会认为another_method是public的,但不是这样。这个'private'存取修饰到作用域(scope)结束,或是直到另一个存取修饰词开始作用。方法预设都是public的:

class MyClass 
  #这个a_method是public的
  def a_method ; true ; end

  private

  #这个another_method是private的
  def another_method ; false ; end
end
public , privateprotected其实也是一种方法,所以可以接受参数。如果你传入一个Symbol,那个该Symbol代表的方法就会改变存取权限。

方法存取权限

在Java里,public表示方法可以被任何人呼叫。protected表示只有这个类别的实例、衍生类别的实例,以及相同package类别的实例可以呼叫,而private表示除了这个类别的实例之外,其他都不行呼叫。

在Ruby中,public还是一样是公开的意思,其他则有一点差异。private表示只有不指定接受者(receiver)时才可以呼叫,也就是只有self可以当成private方法的接受者。

protected也有点不同。一个protected方法除了可以被一个类别或衍生类别的实例呼叫,也可以让另一个相同类别的实例来当做接受者。

来看看Ruby FAQ的例子:

$ irb 
irb ( main ): 001 : 0 > class Test
irb ( main ): 002 : 1 > #预设是public的
irb ( main ): 003 : 1 * def func
irb ( main ): 004 : 2 > 99
irb ( main ): 005 : 2 > end
irb ( main ): 006 : 1 >
irb ( main ): 007 : 1 * def == ( other )
irb ( main ): 008 : 2 > func == other . func
irb ( main ): 009 : 2 > end
irb ( main ): 010 : 1 > end
=> nil
irb ( main ): 011 : 0 >
irb ( main ): 012 : 0 * t1 = Test . new
=> #<Test: 0x34ab50>
irb ( main ): 013 : 0 > t2 = Test . new
=> #<Test:0x342784>
irb ( main ): 014 : 0 > t1 == t2
=> true
irb ( main ): 015 : 0 > #来让`func`变成protected,一样没问题
irb ( main ): 016 : 0 * #因为protected允许其他相同类别的实例呼叫
irb ( main ): 017 : 0 * class Test
irb ( main ): 018 : 1 > protected :func
irb ( main ): 019 : 1 > end
=> Test
irb ( main ): 020 : 0 > t1 == t2
=> true
irb ( main ): 021 : 0 > #来让`func`变成private
irb ( main ): 022 : 0 * class Test
irb ( main ): 023 : 1 > private :func
irb ( main ): 024 : 1 > end
=> Test
irb ( main ): 025 : 0 > t1 == t2
NoMethodError : private method ` func ' called for #<Test:0x342784>
        from (irb):8:in `==
'
        from ( irb ): 25
        from : 0
irb ( main ): 026 : 0 >

类别是开放的

Ruby的 ??类别是开放的,你可以随时打开它新增一点程式或是修改。即使是核心类别如Fixnum或是Object (这是所有类别的父类别)都一样。Ruby on Rails甚至定义了一堆时间方法到Fixnum去,例如:

class Fixnum 
  def hours
    self * 3600 #一小时有多少秒
  end
  alias hour hours
end
  
#从一月一号00:00往后数14个小时
# (你终于醒了吧;)
Time . mktime ( 2006 , 01 , 01 ) + 14 . hours # => Sun Jan 01 14:00:00

有趣的方法名称

在Ruby里,方法名称允许用问号或惊叹号结尾。惯例上,用来回答是非题的方法会用问号结尾(例如Array#empty?会回传true如果方法接收者是空的)。有潜在“危险” (表示有某种副作用,会修改self或参数值。例如exit!等)的方法会用惊叹号结尾。

但是这不表示所有会修改参数的方法一定有惊叹号结尾,例如Array#replace就会替换内容成别的阵列,毕竟replace的意思就是要修改替换自己。

单件方法

单件方法(Singleton methods)是个别物件才有的方法。它们只存在于你要定义的物件之中。

class Car 
  def inspect
    " Cheap car "
  end
end

porsche = Car . new
porsche . inspect # => Cheap car
def porsche.inspect
  " Expensive car "
end

porsche . inspect # => Expensive car

#其他物件就不受影响
other_car = Car . new
other_car . inspect # => Cheap car

Missing 方法

当你呼叫一个不存在的方法,Ruby仍然有办法处理。它会改呼叫method_missing这个方法,并把这个不存在的方法名称传进去当做参数。method_missing预设会丢出一个NameError例外,但是你可以根据你的需求重新定义过,也有许多函式库这么做。这是一个例子:

# id是被呼叫方法的名字,而*符号会收集
#所有传进来的参数变成一个叫做'arguments'的阵列
def method_missing ( id , * arguments )
  puts " Method #{id} was called, but not found. It has " +
       " these arguments: #{arguments.join(", ")} "
end

__ :a , :b , 10
# => Method __ was called, but not found. It has these
# arguments: a, b , 10

以上程式会输出呼叫的细节,但你可以随意定义这个讯息。

传递讯息,不是呼叫函数

一个方法呼叫(method call)其实就是送一个讯息(message)给一个物件:

#这个
1 + 2
#等同于...
1 .+( 2 )
#也等同于:
1 . send " + ", 2

Blocks 也算是物件

程式区块Blocks (或叫做closures)被广泛应用在标准函式库。要执行一个程式区块,可以用 yield,或是透过一个特别的参数让它变成Proc,例如:

def block (  & the_block  ) 
  #在这里面,the_block是被传进来的程式区块
  the_block # return the block
end
adder = block { | a , b | a + ??b }
# adder是一个Proc物件
adder . class # = > Proc

你也可以透过Proc.new 或lambda 在方法外建立程式区块。

同样的,方法也可以当做物件:

method ( :puts ). call  " puts is an object! " 
# => puts is an object!

操作符只是语法包装

大部分的Ruby 操作符(operators)只是一种方法呼叫的语法包装(syntactic sugar),加上一些优先权规则。你要的话,举例来说,我们可以覆写掉Fixnum 的+ 方法:

class Fixnum 
  #可以这么做,但请不要这么改
  def + ( other )
    self - other
  end
end

你不需要C++的operator+等等。

甚至有如阵列存取的[][]=可以定义。要定义一元的+ and – (想想看+1跟-2),你必须分别定义+@-@方法。

以下的操作符则不是语法包装。它们不是方法,不能被覆写定义:

=, .., ..., !, not, &&, and, ||, or, !=, !~, ::

此外+=, *=等只是var = var + other_varvar = var * other_var等的缩写,因此也不能被覆写定义。

更多资料

如果你需要更多Ruby知识,请参考文件

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多