我们开始于一个简单的Ruby程序,我们写一个方法来返回一个字符串,给这个字符串附加一个人名,我们会调用两次这个方法。

def sayGoodnight(name) 
 result = "Goodnight, " + name
 return result
end
# Time for bed...
puts sayGoodnight("John-Boy")
puts sayGoodnight("Mary-Ellen") 

首先,发表一下大致的感观。Ruby语法是干净的,不需要在行尾加上分号,一行一个语句。Ruby注释开始于#号,结束在行尾,代码布局非常适合你,缩排没有什么意义。(译者:译者不太明白这句的意思)

方法是由关键字def来定义的,紧跟着方法的名字(本例中是“sayGoodnight”),方法的参数括在圆括号中。Ruby不使用大括号来划定复合语句和定义的界限,而是简单地用关键词end来结束它们。我们的方法的主体很简单,第一行把字符串“Goodnight, ”和参数name连接,然后把返回值赋值给局部变量result,第二行把result返回给调用者。注意我们不需要声明变量result,当我们赋值给它的时候,它就存在了。

定义了方法后,我们调用了它两次,每次我们都把返回值传递给puts方法,它简单地把参数输出到一个新行。

Goodnight, John-Boy
Goodnight, Mary-Ellen
“puts sayGoodnight(“John-Boy”)”这行包含了两个方法调用,一个调用sayGoodnight另一个调用puts,为什么这两个方法中有一个它的参数括在圆括号中,而另一个却没有圆括号,这种情况纯粹取决于感觉,下面的行是完全等价的:

puts sayGoodnight “John-Boy”
puts sayGoodnight(“John-Boy”)
puts(sayGoodnight “John-Boy”)
puts(sayGoodnight(“John-Boy”))
但是,生活不是那么简单的,优先规则会使哪个参数被哪个方法使用变得复杂,所以我们建议除了极简单的情况,还是使用圆括号为好。

这个例子也展示了Ruby的string对象,有很多种方法来创建字符串对象,不过最通用的莫过于使用字符串的字面值(译者注:literal不好翻,参考其它翻译作品,这里采用字面值的翻译,大概意思就是字符串本身):在单引号或者双引号之间的字符序列。两者之间的不同是Ruby在转换字面值到字符串对象时所处理的数量,单引号的情况下会少。除了一些例外,字符串字面值就是字符串的值。

双引号的情况下,Ruby要做更多的处理,首先,它要寻找替代序列就是反斜杠开头的字符,把它替换成二进制值。最常见的是”\n”,它被替换成换行符,一个字符串如果包含换行符,那么”\n”强制换行。

puts “And Goodnight,\nGrandma”
produces:
And Goodnight,
Grandma
第二件事就是Ruby要修改双引号括住的字符串,字符串中的#{表达式}序列要用表达式的值来替换,我们用这个来重写前面的方法。

def sayGoodnight(name)
 result = "Goodnight, #{name}"
 return result
end 

当Ruby构造这个字符串对象时,它找到name的当前值,并在字符串中替换它。#{…}结构中可以放入任意复杂的表达式,一个简洁的写法是,如果表达式是一个简单的全局变量、实例或者类变量,那么就不必写出大括号。

最后,我们可以让这个方法更加简化,一个Ruby方法的返回值默认是最后被求的表达式的值,所以我们可以省略掉return语句。

def sayGoodnight(name)
 "Goodnight, #{name}"
end 

我们知道这一节很简单,我们讨论了至少一个话题:Ruby名称,为了简单,我们在定义的时候使用了一些术语(像类变量),但是,现在我们要谈到规则,赶在你打游戏之前让我们看看实例变量和类似的一些东西。

Ruby使用一个约定来帮助它区别一个名字的用法:名字前面的第一个字符表明这个名字的用法,局部变量、方法参数和方法名称应该用一个小写字母开头或者一个下划线;全局变量用美元符作为前缀($),而实例变量用@开头,类变量用两个@开头;最后,类名、模块名和常量应该大写字母开头。

词首字母后面可以是字母、数字和下划线的任意组合(规则规定,@后面不可以直接跟数字)。

Example variable and class names
Variables                                         Constants and 
Local         Global     Instance     Class       Class Names  
name          $debug     @name        @@total     PI  
fishAndChips  $CUSTOMER  @point_1     @@symtab    FeetPerMile  
x_axis        $_         @X           @@N         String  
thx1138       $plan9     @_           @@x_pos     MyClass  
_26           $Global    @plan9       @@SINGLE    Jazz_Song  

数组和哈希

Ruby的数组和哈希是有序集合。两者都保存对象的集合,都可以通过键来访问元素。数组的键是一个整数,而哈希支持任何对象作键。数组和哈希都可以生长以便容纳新的元素,对访问元素来说,数组的效率高,但哈希却更灵活。数组和哈希都可以容纳不同类型的对象,你可以使用数组来包含一个整数、一个字符串和一个浮点数,就像你马上看到的那样。

你可以使用数组的字面值来创建和初始化一个数组—-一个方括号括起来的元素集合。有了数组对象,就可以访问单个的数组元素,在方括号中写上一个序号就可以,下面的例子就是这样。

a = [ 1, 'cat', 3.14 ]   # 三个元素的数组  

# 访问第一个元素
a[0]         >>   1  
# 设置第三个元素的值
a[2] = nil  
# 输出数组的值 
a            >>   [1, "cat", nil] 

你可以创建一个空数组,用一个没有元素的数组的字面值,或者用数组对象的构造器,Array.new。

empty1 = []
empty2 = Array.new
有时创建一个字符串的数组会变成一种痛苦,充满了引号和逗号,幸运的是,有一个快捷方式%w帮我们完成。

a = %w{ ant bee cat dog elk }  
a[0]         >>      "ant"  
a[3]         >>      "dog"   

Ruby的哈希和数组相似,一个哈希的字面值使用大括号而不是方括号,字面值至少要为每一个条目提供两个对象:一个是键,一个是值。

举例来说,你可能希望把管弦乐团的乐器归类,使用哈希的话就是:

instSection = {
 'cello'     => 'string',
 'clarinet'  => 'woodwind',
 'drum'      => 'percussion',
 'oboe'      => 'woodwind',
 'trumpet'   => 'brass',
 'violin'    => 'string'
} 

这里译为

instSection = {
 '大提琴'     => '弦乐器',
 '单簧管'     => '木管乐器',
 '鼓'         => '打击乐器',
 '双簧管'     => '木管乐器',
 '小号'       => '铜管乐器',
 '小提琴'     => '弦乐器'
} 

索引哈希使用和数组一样的方括号。

instSection[‘oboe’] >> “woodwind”
instSection[‘cello’] >> “string”
instSection[‘bassoon’] >> nil
最后这个例子显示出,如果使用一个不存在的键来索引哈希,默认返回nil。正常情况下这是很方便的,因为nil用在条件表达式中就是false。有时你希望改变这种默认值,例如,如果你使用哈希来计算每一个键出现的次数,那么比较方便的情况是默认值为0,在你创建一个新的空的哈希时改变默认值是很容易的。

histogram = Hash.new(0)
histogram[‘key1’] >> 0
histogram[‘key1’] = histogram[‘key1’] + 1
histogram[‘key1’] >> 1

控制结构

Ruby包括所有常见的控制结构,像if语句和while循环,Java、C、Perl程序员会感到这些语句的主体周围缺少了大括号,在Ruby中要表示一段主体的结束,我们使用end关键字。

if count > 10
 puts "Try again"
elsif tries == 3
 puts "You lose"
else
 puts "Enter a number"
end 

同样,while语句也由end来结束。

while weight < 100 and numPallets <= 30
 pallet = nextPallet()
 weight += pallet.weight
 numPallets += 1
end 

如果if语句或者while语句的主体是一个简单的表达式,这时Ruby语句修饰符就是一个很有用的快捷方式。简单地写下这个表达式,后面跟上if或者while和条件语句。下面的例子是一个简单的if语句:

if radiation>3000
  puts "Danger,Will Robinson"
end 

再来一次,这次用语句修饰符来重写。

puts “Danger,Will Robinson” if radiation>3000
同样的,一个while循环如下:

while square < 1000
  square = square*square
end 

变得更简洁,

square = square*square while square < 1000 这些语句修饰符看起来和Perl程序员使用的很相似。 正则表达式 Ruby的大多数内置类型对所有的程序员来说都很熟悉,大部分语言都有字符串、整数、浮点数等等类型。但是,直到Ruby产生的时候,正则表达式还只被所谓的脚本语言支持,像Perl,Python,awk等,这应该让人惭愧:正则表达式虽然晦涩难懂,但确实是处理文本的绝佳工具。 要写正则表达式的话能写整整一本书(像《Mastering Regular Expressions》),所以我们不会覆盖所有的内容而只是写一个短篇。我们来看一些正则表达式的例子,你要找到完整的资料看56页的正则表达式。 一个正则表达式就是在一个字符串中用来匹配的特定的模式字符。在Ruby中,用两个斜线括住的模式来显式地创建一个正则表达式(/pattern/),而且,由于Ruby之所以为Ruby的原因,正则表达式当然的也是对象,并且能被处理。 举例,你要写一个模式来匹配一个包含"Perl"或者"Python"的字符串,就用下面的正则表达式: /Perl|Python/ 斜线界定了模式,它包括我们要匹配的两个字符串,用管道符("|")分隔开,你可以在模式中使用圆括号,就像在算术表达中那样,你可以把模式写成这样: /P(erl|ython)/ 也可以在模式中重复声明,/ab+c/匹配一个字符串,它包含一个"a",然后是一个或者多个"b",然后是一个"c"。如果把加号改成星号,就是/ab*c/那么创建的正则表达式是匹配一个"a",0或者更多的"b",和一个"c"。 你也可以用一个模式来匹配一组字符,常见的如"\s"匹配空白字符(空格、Tab、换行符等),"\d"匹配所有的数字,"\w"匹配所有的可打印字符,简单的一个字符"."(一个点号)匹配任意字符。 我们把它们放在一起就可以组合成非常有用的正则表达式。

/\d\d:\d\d:\d\d/    #  类似12:34:56这样的时间
/Perl.*Python/            #  Perl,0或者更多的字符,然后是Python
/Perl\s+Python/           #  Perl,1个或者更多空格,然后是Python
/Ruby (Perl|Python)/      #  Ruby,1个空格,然后是Perl或者Python 

匹配算符”=~”用在正则表达式匹配一个字符串的时候。当模式在字符串中被匹配到后,=~返回它开始的位置,否则返回nil。这意味着你可以在if语句或者while语句中使用正则表达式。例如,下面的代码片断显示当一个字符串中包含”Perl”或者”Python”时的情况。

if line =~ /Perl|Python/
 puts "Scripting language mentioned: #{line}"
end 

字符串中与正则表达式匹配的部分也可以被替换成不同的文本,这要使用Ruby的替代方法。

line.sub(/Perl/, ‘Ruby’) # 用”Ruby”替换第一个”Perl”
line.gsub(/Python/, ‘Ruby’) # 用”Ruby”替换所有的”Python”
随着本书的进行,我们还要讨论正则表达式的更多内容。
代码块和迭代器

这一节简短介绍一下Ruby的一个非常特别的功能,我们来认识一下代码块:可以和方法调用关联的一系列代码,就好像这些代码是方法的参数一样,这是一个令人难以置信的强大特性。你可以使用代码块实现回调(但不像Java的匿名内部类那么简单),传递一系列代码(但要比C的函数指针更加复杂),和实现迭代器。

代码块是用大括号或者do…end括起来的一系列代码。

{ puts "Hello" }       # 这是一个代码块
do                           #
 club.enroll(person)        # 这也是代码块
 person.socialize           #
end                          # 

一旦你创建了一个代码块,就可以把它和一个方法调用关联在一起。那个方法能够调用代码块一次或者更多次,用Ruby的yield语句。下面的例子显示了这个过程。我们定义一个方法,这个方法调用yield两次。然后我们调用这个方法,把代码块放在同一行中方法调用的后面(也是方法的所有参数的后面)。[有些人喜欢把和方法关联的代码块当作是一种传递过来的参数。它们虽然是一个级别的,但这没有显示出所有的内涵。最好把代码块和方法当成是协同工作的关系,在它们之间控制在来回交换。]

def callBlock
 yield
 yield
end
callBlock { puts "In the block" } 
结果:
In the block
In the block 

看看代码块中的代码(puts “In the block”) 是如何被执行两次的,就是对yield的每一次调用。

你可以在调用yield时给出参数,这些参数传递给代码块。在代码块中,列举变量的名字来接受参数,这些参数被用”|”括着。

 def callBlock
   yield , 
 end
 callBlock { |, | ... } 

代码块贯穿在实现迭代器的Ruby库中,迭代器就是一种方法,用来连续返回某种集合的元素,比如一个数组。

a = %w( ant bee cat dog elk ) # 创建一个数组
a.each { |animal| puts animal } # 迭代所有的内容

produces:
ant
bee
cat
dog
elk
我们来看看实现Array类的each迭代器的可能的方法,我们要用到前面的例子。each迭代器遍历数组的每个元素,每次都调用yield,类似的代码可能会是下面这样:

# 在Array类中...
def each
 for each element
   yield(element)
 end
end 

这样你就可以使用数组的each方法来迭代数组元素提供给代码块,代码块依次在每个元素返回时被调用一次。

[ 'cat', 'dog', 'horse' ].each do |animal|
 print animal, " -- "
end 
结果:
cat -- dog -- horse -- 

类似的,内置在语言比如C或者Java中的许多循环结构在Ruby中就是简单的方法调用,这个方法调用所关联的代码块0次或者更多次。

5.times {  print "*" }
3.upto(6) {|i|  print i }
('a'..'e').each {|char| print char } 
结果: 
*****3456abcde 

在这里,我们让数字5调用一个代码块5次,然后让数字3调用一个代码块,传递给它连续的数值直到6,最后,字符”a”到”e”的区间使用each方法调用一个块。
读写

Ruby带着很完善的I/O库,不过,这本书的大部分例子只介绍了一些很简单的方法,我们已经看到过两个用来输出的方法,puts把它的所有参数写出来,每一个都加入一个新行,print也写出它的参数,不过没有新行。它们两个都能向任意的I/O对象写入,不过默认是写入控制台。

另一个常用的输出方法是printf,它按格式输出参数(就像C或者Perl的printf)。

printf “Number: %5.2f, String: %s”, 1.23, “hello”

结果:
Number: 1.23, String: hello
这个例子中,格式字符串”Number: %5.2f, String: %s” 告诉printf用一个浮点数(总共允许5位,小数点后两位)和一个字符串来代替。

有很多种方式来把输入读取到你的程序中,也许,最传统的就是使用gets例程,它从你的程序的标准输入流中返回下一行。

line = gets
print line
gets例程有一个附带效果,它除了返回读取的行,还把它储存到全局变量$_中,这个变量很特殊,在很多环境中它作为默认变量使。如果你调用print而没有带参数,它显示$_的内容;如果你写一个if或者while语句时仅仅使用一个正则表达式作为条件,那么这个表达式匹配的对象是$_。尽管一些纯粹主义者把这看作是令人讨厌的野蛮行径,但是这些简写确实又能帮助我们写出简洁的程序来。例如,下面的程序现实输入流中的所有行中包含”Ruby”单词的行。

while gets           # assigns line to $_
 if /Ruby/          # matches against $_
   print            # prints $_
 end
end 

Ruby方式的写法是使用迭代器:

ARGF.each { |line| print line if line =~ /Ruby/ }
这里使用了预定义对象ARGF,它描述可以被程序读取的输入流。
就是这些,我们已经完成了这个闪电般的Ruby基础特性的旅行,我们简单地看了一下对象、方法、字符串、容器和正则表达式,看了一些简单的控制结构,和迭代器。

以上内容转载自网络,小子觉得对了解ruby有一定用途。