[翻译]Ruby教程12——面向对象

Ruby的面向对象编程

在这部分的Ruby教程我们将讨论面向对象编程。


编程语言有过程式编程、函数式编程和面向对象编程范式。Ruby中面向对象语言并包含了一些函数式和过程式。


面向对象(OOP)是一种使用对象及其接口来设计应用程序和计算机程序的编程范式。


面向对象的基本概念如下:



  • 抽象(Abstraction)

  • 多态(Polymorphism)

  • 封装(Encapsulation)

  • 继承(Inheritance)


抽象是对于现实中复杂的问题通过适当的建模将其简化。多态是使用相同的操作符或者函数对不同的输入数据进行不同处理。封装是将一个类的具体实现对其他对象进行隐藏。继承是一种使用已经定义的类来创建新的类的方式。


对象


对象是Ruby面向对象程序的基本组成。一个对象包含了数据和方法。对象之间通过方法进行交流。每个对象可以接收消息、发送消息和处理数据。


创建一个对象需要两步。首先定义一个类。类是对象的模板。它是一张蓝图,用来描述这个类的所有对象的状态和行为。一个类可以创建多个对象。运行时创建的对象称为这个类的实例。


#!/usr/bin/ruby

class Being

end

b = Being.new
puts b

第一个例子我们创建了一个简单的对象。


class Being

end

定义一个简单的类,内容为空。表示它没有任何数据和方法。


b = Being.new

创建一个Being类的新实例。这里我们使用new方法,新创建的对象保存在变量b中。


puts b

在终端上打印对象的基本描述。当我们打印一个对象时,实际上是调用的to_s方法。但是我们没有任何的定义,因为每个创建的对象都是继承自Object。它有一些基本的函数,to_s是其中一个。


$ ./simple.rb
#<Being:0x9f3c290>

我们得到这个对象的类名。


构造函数


构造函数是一个特殊的方法。它在对象创建时自动执行。它没有返回值。构造函数的目的是初始化对象的状态。在Ruby中构造函数名为intialize


构造函数不能被继承。父对象的构造函数是通过super方法来调用。它们的调用顺序与继承顺序一致。


#!/usr/bin/ruby

class Being

def initialize
puts “Being is created”
end

end

Being.new

定义一个Being类。


class Being

def initialize
puts “Being is created”
end

end

Being定义了一个构造函数名为initialize。它在终端上打印一条信息。Ruby中方法定义位置defend关键字之间。


Being.new

创建一个Being类的实例对象。在对象初创时构造函数将被调用。


$ ./constructor.rb

Being is created


程序的输出。




对象的属性是绑定在对象里的数据项。这些数据项也称为实例变量(instance variables)或者成员字段(member fields)。实例变量在类中定义但是各个对象都有单独的复本。


下面的例子我们初始化类的成员数据。变量初始化是构造函数的典型工作。


#!/usr/bin/ruby

class Person

def initialize name
@name = name
end

def get_name
@name
end

end

p1 = Person.new “Jane”
p2 = Person.new “Beky”

puts p1.get_name
puts p2.get_name

上面的例子定义了一个Person类,并且有一个实例变量。


class Person

def initialize name
@name = name
end

Person的构造函数设置了一个实例变量name。构造函数的name参数是在创建时传递的。构造函数是在实例对象创建时调用。@name是一个实例变量。在Ruby中实例变量以@字符开头。


def get_name
@name
end

get_name方法返回成员字段。在Ruby蝇成员字段只能通过方法来访问。


p1 = Person.new “Jane”
p2 = Person.new “Beky”

我们创建了Person类的两个对象。每个对象的构造函数都传递了一个字符串参数。


puts p1.get_name
puts p2.get_name

通过调用每个对象的get_name方法来打印成员字段。


$ ./person.rb

Jane

Beky


从程序的输出看到每个实例都有自己的name成员字段。




我们可以创建一个对象而不调用构造函数。Ruby有一个特殊的allocate方法。allocate方法为新的对象分配空间而不调用initialize


#!/usr/bin/ruby

class Being

def initialize
puts “Being created”
end
end


b1 = Being.new
b2 = Being.allocate
puts b2

这个例子我们创建了两个对象。第一介对象使用new方法,第二个对象使用allocate方法。


b1 = Being.new

这里我们通过new关键字创建一个实例对象。构造函数initialize将会调用,并且在终端上打印消息。


b2 = Being.allocate
puts b2

这里使用allocate方法,没有调用构造函数。使用puts关键字调用对象的to_s方法将其显示。


$ ./allocate.rb
Being created
#<Being:0x8ea0044>

程序的输出。


构造函数重载


重载构造函数可以使用类有多种类型的构造函数。这样我们可以使用不同数量或者不同类型的参数来创建对象。


Ruby没有我们所知的其他语言那样的构造函数重载。在Ruby中这种行为可以通过一些有默认值的扩展参数来模拟。


#!/usr/bin/ruby


class Person

def initialize name=”unknown”, age=0
@name = name
@age = age
end

def to_s
“Name: #{@name}, Age: #{@age}”
end

end

p1 = Person.new
p2 = Person.new “unknown”, 17
p3 = Person.new “Becky”, 19
p4 = Person.new “Robert”

p p1, p2, p3, p4

这个例子展示了模拟构造函数的重载。当name参数没有指定时使用“unknow”代替,对于age使用0。


def initialize name=”unknown”, age=0
@name = name
@age = age
end

这个构造传入两个参数。它们都有默认值。当我们创建对象没有指定值时就使用默认值。注意参数顺序必须一致。第一个是name,第二介是age


p1 = Person.new
p2 = Person.new “unknown”, 17
p3 = Person.new “Becky”, 19
p4 = Person.new “Robert”

p p1, p2, p3, p4

我们创建了四个对象。构造函数传入了不同个数的参数。


$ ./consover.rb

Name: unknown, Age: 0

Name: unknown, Age: 17

Name: Becky, Age: 19

Name: Robert, Age: 0


例子的输出结果。


方法


方法是定义在类里面的函数。它们用于对对象的属性执行一些操作。方法在面向对象范式的封装性中必不可少。例如我们AccessDatabase类中有一个connect方法,我们不需要关心这个方法到底是如何连接数据库的。我们仅需要知道使用这个方法连接数据库。这对程序功能的划分必不可少,尤其是大的应用程序。


在Ruby中数据仅能够通过方法访问。


#!/usr/bin/ruby

class Person

def initialize name
@name = name
end

def get_name
@name
end

end

per = Person.new “Jane”

puts per.get_name
puts per.send :get_name

这个例子展示了调用方法的两个基本方式。


puts per.get_name

通常的方式是在对象后面使用点操作符。


puts per.send :get_name

另种方式是使用内建的send方法。它将方法名符号作为参数传入。




方法通常对对象的数据进行一些操作。


#!/usr/bin/ruby

class Circle

@@PI = 3.141592

def initialize
@radius = 0
end

def set_radius radius
@radius = radius
end

def area
@radius @radius @@PI
end

end


c = Circle.new
c.set_radius 5
puts c.area

这个例子的代码我们定义了一个Circle类两个方法。


@@PI = 3.141592

我们在Circle类中定义了一个@@PI变量。类变量以@@开头。类变量是属于类的,每个对象都可以访问它们的类变量。我们@@PI来计算圆的面积。


def initialize
@radius = 0
end


定义了一个成员字段。它是圆的半径。如果我们想在外部修改这个变量,我们必须使用公开的set_radius方法。这个数据是受保护的。


def set_radius radius
@radius = radius
end

这是set_radius方法。它为@radius实例变量设置一个新的值。


def area
@radius @radius @@PI
end

area方法返回圆的面积。


c = Circle.new
c.set_radius 5
puts c.area

我们创建一个Circle类的实例对象,并且通过set_radius方法设置它的半径。


$ ./circle.rb

78.5398


例子的输出结果。


访问修饰符


访问修饰符设置成员和方法的可见性。Ruby有三种访问修饰符:publicprotectedprivate。在Ruby中所有的数据都是私有的。访问修饰符可以仅对方法使用。Ruby中的方法是公开的,除非使用了其他修饰符。


公开的方法在类的内部和外部都可以访问。保护和私有的方法略微不同。都不能在类外部访问,仅能在这个类和它的子类或者父类内部访问。


注意与其他面向对象编程语言不同,继承不会充当访问修饰符。仅有两件事很重要。第一,我们是否可以在类的内部或者外部访问方法。第二,是否我们要使用或者不使用self关键字。


访问修饰符保护数据避免受到意外的修改。使用程序更健壮。实现一些主要用于修改数据的方法。这些方法最好是私有的。只有真正需要修改才将接口公开给用户。多年来用户习惯使用特殊方法并对打破向后兼容普遍不满。


#!/usr/bin/ruby

class Some

def method1
puts “public method1 called”
end

public

def method2
puts “public method2 called”
end

def method3
puts “public method3 called”
method1
self.method1
end
end

s = Some.new
s.method1
s.method2
s.method3

这个例子解释了Ruby公有方法的用法。


def method1
puts “public method1 called”
end

method1是公有的,尽管我们没有使用public修饰符。因为方法默认都是公有的,除非指明为其他。


public

def method2
puts “public method2 called”
end



public关键字之后的方法是公有的。


def method3
puts “public method3 called”
method1
self.method1
end

在公有方法method3中我们通过使用和没有使用self关键字调用了另一个公有方法。


s = Some.new
s.method1
s.method2
s.method3

公有方法是仅能够在类外部调用的方法。


$ ./public_methods.rb
public method1 called
public method2 called
public method3 called
public method1 called
public method1 called


例子运行结果。




下一个例子看私有方法。


#!/usr/bin/ruby


class Some

def initialize
method1
# self.method1
end

private

def method1
puts “private method1 called”
end

end


s = Some.new
# s.method1

私有方法是Ruby中严厉的方法。它们只能够在类内部调用并且不能使用self关键字。


def initialize
method1
# self.method1
end

在构造函数方法中我们调用了私有方法method1。使用self调用的被注释了。私有方法不能指定接收者。


private

def method1
puts “private method1 called”
end

private关键字之后的是私有方法。


s = Some.new
# s.method1

创建了一个Some类的实例对象。在外部调用这个方法是禁止的,如果将这行取消注释Ruby解释器会报错。


$ ./private_methods.rb

private method called


输出结果。




最后我们使用保护方法。保护方法和私有方法的区别很小。保护方法与私有方法相似,不过它们可以通过self关键字调用。


#!/usr/bin/ruby

class Some

def initialize
method1
self.method1
end

protected

def method1
puts “protected method1 called”
end

end


s = Some.new
# s.method1

上面的例子展示了保护方法的用法。


def initialize
method1
self.method1
end

保护方法可以使用和不使用self关键字。


protected

def method1
puts “protected method1 called”
end

保护方法以protected关键字开头。


s = Some.new
# s.method1

保护方法不能在类外部调用。取消注释会报错。


继承


继承是使用已经定义的类来构造新的类的方式。新构建的类称为派生类。派生自的类称为基类。继承的好处是代码利用,减少程序的复杂性。派生类(后代)覆盖或者扩展基类(祖先)的函数。


#!/usr/bin/ruby

class Being

def initialize
puts “Being class created”
end
end

class Human < Being

def initialize
super
puts “Human class created”
end
end

Being.new
Human.new

这个程序我们定义了两个类:一个基类Being和一个派生类Human


class Human < Being

Ruby中使用<操作符创建继承关系。Human类继承自Being类。


def initialize
super
puts “Human class created”
end

super方法调用父类的构造函数。


Being.new
Human.new

实例化了Being类和Human类。


$ ./inheritance.rb

Being class created

Being class created

Human class created


首先创建Being类。基类Human同样也调用了父类的构造函数。




一个对象的关系可能很复杂。一个对象可以有多个祖先。Ruby有ancestors方法获取一个类的祖先列表。


每个Ruby对象都是ObjectBaseObjectKernel的后代。它们内建于Ruby语言的内核中。


#!/usr/bin/ruby


class Being
end

class Living < Being
end

class Mammal < Living
end

class Human < Mammal
end


p Human.ancestors

这个例子中定义了四个类。HumanMammalLivingBeing


p Human.ancestors

打印Human类的祖先。


$ ./ancestors.rb

[Human, Mammal, Living, Being, Object, Kernel, BasicObject]


Human类有三个自定义的和三个内建的祖先。




一个更复杂的例子。


#!/usr/bin/ruby

class Being

@@count = 0

def initialize
@@count += 1
puts “Being class created”
end

def show_count
“There are #{@@count} beings”
end

end

class Human < Being

def initialize
super
puts “Human is created”
end
end

class Animal < Being

def initialize
super
puts “Animal is created”
end
end

class Dog < Animal

def initialize
super
puts “Dog is created”
end
end

Human.new
d = Dog.new
puts d.show_count

我们定义了四个类。继承的层级有点复杂。HumanAnimal继承自BeingDog继承自Animal。我们还使用了类变量来统计beings的创建个数。


@@count = 0

我们定义一个类变量。它用于统计beings的创建个数。


def initialize
@@count += 1
puts “Being class created”
end

每次Being类实例化时我们将@@count变量加1。这使用我们可以跟踪实例创建的个数。


class Animal < Being


class Dog < Animal


Animal继承自BeingDog继承自Animal。进一步的Dog也继承自Being


Human.new
d = Dog.new
puts d.show_count

我们通过HumanDog创建实例。然后调用Dog对象的show_count方法。Dog类没有该方法,将调用Being类的。


$ ./inheritance2.rb
Being class created

Human is created

Being class created

Animal is created

Dog is created

There are 2 beings


Human对象调用了两个构造函数。Dog对象调用了三个构造函数。创建了两个Being实例。




方法和数据成员可见性在继承中不起作用。这与其他通常的面向对象编程语言是显著的不同。


在C#或者Java中公有的和保护的数据成员和方法可以被继承,私有的不能。与这相比,在Ruby中私有的数据成员和方法也可以被继承。数据成员和方法的可见性不会受继承的影响。


#!/usr/bin/ruby

class Base

def initialize
@name = “Base”
end

private

def private_method
puts “private method called”
end

protected

def protected_method
puts “protected_method called”
end

public

def get_name
return @name
end
end


class Derived < Base

def public_method
private_method
protected_method
end
end

d = Derived.new
d.public_method
puts d.get_name

这个例子中有两个类。Derived继承乍Base。它继承了三个方法和一个数据字段。


def public_method
private_method
protected_method
end

Derived类的public_method调用了一个私有方法和一个保护方法。它们定义在父类中。


d = Derived.new
d.public_method
puts d.get_name

创建一个Derived类的实例。调用public_method方法和get_name方法,它返回私有的实例变量@name。记住Ruby中所有的实例变量都是私有的。get_name方法返回这个变量不管@name是私有的还是在父类中定义的。


$ ./inheritance3.rb

private method called

protected_method called

Base


输出结果证实了在Ruby中公有的、保护的、私有的方法和私有的成员字段都能被继承。


super方法


super方法调用父类的同名方法。如果没有传递参数它将自动的把当前的所有参数传入。如果写为super()则没有参数传入。


#!/usr/bin/ruby

class Base

def show x=0, y=0
p “Base class, x: #{x}, y: #{y}”
end
end

class Derived < Base

def show x, y
super
super x
super x, y
super()
end
end


d = Derived.new
d.show 3, 3

这个例子有两个类一个继承。它们都定义了show方法。这个方法在Derived类中使用super调用了父类的方法。


def show x, y
super
super x
super x, y
super()
end

super不带参数则会传递将当前传入的参数,这里是x=3、y=3。super()方法不传递参数。


$ ./super.rb

“Base class, x: 3, y: 3”

“Base class, x: 3, y: 0”

“Base class, x: 3, y: 3”

“Base class, x: 0, y: 0”


输出结果。


这是Ruby的面向对象的第一部分。




原文地址: http://zetcode.com/lang/rubytutorial/oop/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程11——哈希表

哈希表

这部分的教程我们将提到哈希表。哈希表是一个键-值对的集合。与数组相似,不同与数组的是哈希表的索引是确定的,数组的只能是整数。哈希表有时称为相关联的数组。


哈希表是非常有用的集合。它有许多方法供程序员使用。


创建哈希表


有两种创建哈希表的方式:使用new关键字或者哈希表的字面量。


#!/usr/bin/ruby

names = Hash.new
names[1] = “Jane”
names[2] = “Thomas”

puts names

第一个脚本创建了一个哈希表并添加了两个键-值对。


names = Hash.new

创建一个哈希对象。


names[1] = “Jane”
names[2] = “Thomas”

添加两对值。数字1、2是哈希表的键。键放在中括号里。names是属于键的值。


puts names

打印哈希表。


$ ./create.rb
{1=>”Jane”, 2=>”Thomas”}

从输出结果我们看到一个哈希表是用花括号括起来的。键和值是使用=>符号成对搭配。




store方法可以用于给哈希表设置一些初始值。它可以代替花括号。


#!/usr/bin/ruby

names = Hash.new
names.store(1, “Jane”)
names.store(2, “Thomas”)
names.store(3, “Rebecca”)

puts names

创建了一个相似的脚本,这次我们使用store方法。


names.store(1, “Jane”)

store方法的第一个参数是键,第二个参数是值。




第三个脚本我们使用字面符来创建哈希表。它的值是用花括号括起来的。并且键和值是用=>符号成对分配。


#!/usr/bin/ruby

domains = { “de” => “Germany”,
“sk” => “Slovakia”,
“hu” => “Hungary”,
“us” => “United States”,
“no” => “Norway”
}

puts domains[“de”]
puts domains[“sk”]

创建哈希表domains包含5对内容。这里键和值都是字符串。


$ ./create3.rb

Germany

Slovakia


例子的输出结果。


基本用法


这一节我们展示Ruby哈希表的一些很基础的方法。


#!/usr/bin/ruby

names = Hash.new

names[1] = “Jane”
names[2] = “Thomas”
names[3] = “Robert”
names[4] = “Julia”
names[5] = “Rebecca”

puts “The size of the hash is #{names.size}”

puts names.keys.inspect
puts names.values.inspect

上面的脚本中创建了一个有5个值的哈希表,介绍了哈希表的三个方法。


puts “The size of the hash is #{names.size}”

size方法返回哈希表的大小。与length方法相同。


puts names.keys.inspect
puts names.values.inspect

keys方法返回哈希表的所有键。以此类推,values方法返回哈希表的所有值。返回的数据是一个数组形式的。为了使输出更加可读我们使用了inspect方法。


$ ./basic.rb

The size of the hash is 5

[1, 2, 3, 4, 5]

[“Jane”, “Thomas”, “Robert”, “Julia”, “Rebecca”]


例子的输出结果。注意最后两个方法是两个数组。




这节的第二个例子展示了哈希表实例的三个方法。


#!/usr/bin/ruby

names1 = Hash.new

names1[1] = “Jane”
names1[2] = “Thomas”
names1[3] = “Robert”
names1[4] = “Julia”
names1[5] = “Rebecca”

names2 = names1.dup

puts names1.eql? names2

puts names1.empty?
names1.clear
puts names1.empty?

创建了一个names哈希表,对该对象调用了三个方法。


names2 = names1.dup

通过dup方法创建了一个副本。


puts names1.eql? names2

eql?方法比较两个哈希表对象是否相等。这里是相同的,打印true。


puts names1.empty?

empty?方法检查哈希表是否为空。这行打印false,因为names1有5项数据。


names1.clear
puts names1.empty?

clear方法删除哈希表的所有内容。接着调用empty?方法返回true。


$ ./basic2.rb

true

false

true


输出结果。




有一个方法可以判断一个键或者值是否在这个哈希表中。


#!/usr/bin/ruby

domains = { :de => “Germany”, :sk => “Slovakia”,
:no => “Norway”, :us => “United States”
}

puts domains.has_key? :de
puts domains.include? :no
puts domains.key? :me
puts domains.member? :sk

puts domains.has_value? “Slovakia”
puts domains.value? “Germany”

创建了一个有4对数据的哈希表domains。键是用的符号,因为它更有效。


puts domains.has_key? :de
puts domains.include? :no
puts domains.key? :me
puts domains.member? :sk

这里我们用了四个方法来判断哈希表是否包含该键。它们的功能都是相同的。


puts domains.has_value? “Slovakia”
puts domains.value? “Germany”

这里用了两个方法检查这两个字符串是否在哈希表中。


$ ./has.rb

true

true

false

true

true

true


输出结果。




这节的最后一个例子,我们将从哈希表中读取内容。


#!/usr/bin/ruby

stones = { 1 => “garnet”, 2 => “topaz”,
3 => “opal”, 4 => “amethyst”
}

puts stones.fetch 1
puts stones[2]
puts stones.values_at 1, 2, 3

这个脚本展示了三个读取值的方法。


puts stones.fetch 1

fetch方法通过给定的键读取值。


puts stones[2]

也可以使用中括号来获取一个值,这行会打印“topaz”


puts stones.values_at 1, 2, 3

values_at方法可以一次获取多个值。这个方法通过给定的键返回一个数组包含了对应的值。


$ ./read.rb

garnet

topaz

garnet

topaz

opal


例子的输出结果。


循环遍历哈希表


有一些方法可以用于循环遍历哈希表。


#!/usr/bin/ruby

stones = { 1 => “garnet”, 2 => “topaz”,
3 => “opal”, 4 => “amethyst”
}

stones.each { |k, v| puts “Key: #{k}, Value: #{v}” }
stones.each_key { |key| puts “#{key}” }
stones.each_value { |val| puts “#{val}” }
stones.each_pair { |k, v| puts “Key: #{k}, Value: #{v}” }

上面的例子我们展示的四个方法。用这些方法显示了所有的键、值。


stones.each { |k, v| puts “Key: #{k}, Value: #{v}” }

each方法对每个键都调用了给定的代码块,键-值对作为参数传递。


stones.each_key { |key| puts “#{key}” }

我们使用each_key方法循环遍历了哈希表所有的键。将它们打印在终端上。


stones.each_value { |val| puts “#{val}” }

each_value用于循环遍历哈希表所有的值。


stones.each_pair { |k, v| puts “Key: #{k}, Value: #{v}” }

each_pair方法与each方法相同。


$ ./loop.rb

Key: 1, Value: garnet

Key: 2, Value: topaz

Key: 3, Value: opal

Key: 4, Value: amethyst

1

2

3

4

garnet

topaz

opal

amethyst

Key: 1, Value: garnet

Key: 2, Value: topaz

Key: 3, Value: opal

Key: 4, Value: amethyst


输出结果。


删除键值对


接下来的例子关注哈希表的删除。


#!/usr/bin/ruby

names = Hash.new

names[1] = “Jane”
names[2] = “Thomas”
names[3] = “Robert”
names[4] = “Julia”
names[5] = “Rebecca”

names.delete 4
names.shift

puts names

这个脚本我们使用了两个方法:deleteshiftdelete方法是删除指定的键的值,并将其返回。shift方法删除哈希表的第一对键值,并将其作为数组返回。


names.delete 4

删除4 => “Julia”这对值。


names.shift

这行代码删除1 => “Jane”这对值。


$ ./deleteitem.rb
{2=>”Thomas”, 3=>”Robert”, 5=>”Rebecca”}

输出结果显示还剩的内容。




rejectdelete_if方法可以从哈希表中移除多项内容。如果这些方法所给定的代码块中的条件式返回true,则删除对应的键值对。这两个方法有个重要区别。reject方法作用于复本,delete_if作用于原对象。


#!/usr/local/bin/ruby

names1 = Hash.new

names1[1] = “Jane”
names1[2] = “Thomas”
names1[3] = “Robert”
names1[4] = “Julia”
names1[5] = “Rebecca”

puts names1.reject { |k, v| v =~ /R./ }
puts names1
puts names1.delete_if { |k, v| k<=3 }
puts names1

这个例子使用上面的方法删除多项键值对。


puts names1.reject { |k, v| v =~ /R./ }

reject方法移除所有满足代码块中正则式的值,并返回修改后的哈希表,原哈希表不变。


puts names1

这行的输出证实了原哈希表没有改变。


puts names1.delete_if { |k, v| k<=3 }

这里我们删除键小于等于3的键值对。这个方法修改了原对象。


$ ./massdelete.rb
{1=>”Jane”, 2=>”Thomas”, 4=>”Julia”}
{1=>”Jane”, 2=>”Thomas”, 3=>”Robert”, 4=>”Julia”, 5=>”Rebecca”}
{4=>”Julia”, 5=>”Rebecca”}
{4=>”Julia”, 5=>”Rebecca”}

输出结果。


添加内容


mergeupdate方法可以给哈希表添加键值对。


#!/usr/bin/ruby

names1 = Hash.new

names1[1] = “Jane”
names1[2] = “Thomas”

names2 = Hash.new

names2[3] = “Robert”
names2[4] = “Julia”

names = names1.merge names2
puts names

names = names1.update names2
puts names

这个脚本中我们创建了两个哈希表,然后对它们调用了mergeupdate方法。


names = names1.merge names2
puts names

合并names1names2,结果分配给names


$ ./merge.rb
{1=>”Jane”, 2=>”Thomas”, 3=>”Robert”, 4=>”Julia”}
{1=>”Jane”, 2=>”Thomas”, 3=>”Robert”, 4=>”Julia”}

最后的哈希表包含了names1names2的内容。


merge与merge!


最后一节,我们重述一下Ruby的习俗。一些Ruby的方法以感叹号结尾。这个标志在语法上没有意义,它只是表明调用这个方法会修改对象的内容。


#!/usr/bin/ruby

names1 = Hash.new

names1[1] = “Jane”
names1[2] = “Thomas”

names2 = Hash.new

names2[3] = “Robert”
names2[4] = “Julia”

names = names1.merge names2
puts names
puts names1

names = names1.merge! names2
puts names
puts names1

我们展示了mergemerge!方法的不同。


names = names1.merge names2

merge不会修改names1,它作用于一个新的复本。


names = names1.merge! names2

merge!方法作用于原对象。names1被修改了。


$ ./merge2.rb
{1=>”Jane”, 2=>”Thomas”, 3=>”Robert”, 4=>”Julia”}
{1=>”Jane”, 2=>”Thomas”}
{1=>”Jane”, 2=>”Thomas”, 3=>”Robert”, 4=>”Julia”}
{1=>”Jane”, 2=>”Thomas”, 3=>”Robert”, 4=>”Julia”}

merge2.rb程序的输出。


在这章我们学习了Ruby的哈希表。




原文地址: http://zetcode.com/lang/rubytutorial/hashes/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程10——数组

数组

这一部分的教程将介绍数组。数组是有序对象的集合。


一个这是在某个时刻只能保存一项数值。不过数组可以保存多项。这些数据项被称为数组的元素。数组可以保存任何类型的数据。每个元素可以使用索引来引用。第一个元素的索引值为0。


注意Ruby的数组与C、C++或者Java中的数组有很大的不同。


#!/usr/bin/ruby

nums = [1, 2, 3, 4, 5]

nums.each do |num|
puts num
end

我们的第一个例子创建了一个包含5个整数的数组,然后在终端上打印这些元素。


nums = [1, 2, 3, 4, 5]

这行创建了一个包含5个整数的数组,每个元素使用逗号分隔开来。


nums.each do |num|
puts num
end

我们使用each方法遍历数组并在终端上打印每个元素。


$ ./array.rb

1

2

3

4

5


程序的输出结果。


创建数组


Ruby中数组是一个对象,可以使用new方法进行实例化。


#!/usr/bin/ruby

nums = Array.new

nums.push 1
nums.push 2
nums.push 3
nums.push 4
nums.push 5

puts nums

这个脚本中我们创建了一个数组nums,然后往里面添加了5个整数。


nums = Array.new

创建数组对象。


nums.push 1

push方法是往数组尾部添加一项数据。




我们继续使用new方法创建数组。


#!/usr/bin/ruby

a1 = Array.new
a2 = Array.new 3
a3 = Array.new 6, “coin”
a4 = Array.new [11]
a5 = Array.new (15) {|e| ee}

puts [a1, a2, a3, a4, a5].inspect

Array类的new方法有一些选项。


a1 = Array.new

创建一个空的数组,支持稍后再往其中填充数据。


a2 = Array.new 3

创建一个数组包含3个nil对象。


a3 = Array.new 6, “coin”

创建一个数组包含6个“coin”字符串。第一个选项是数组的大小;第二个选项是填充的对象。


a4 = Array.new [11]

第四个数组只有一项数据。


a5 = Array.new (15) {|e| ee}

创建一个有15个元素的数组,第个元素都是在代码块中创建。这里计算了序号数的平方。


puts [a1, a2, a3, a4, a5].inspect

将所有的数组放在一个数组中。数组是可以放入另一个数组里的。然后调用数组的inspect方法,它将对其所有的元素都调用该方法。inspect方法将返回代表这个数组的字符串。当我们需要快速检查数组内容时它非常有用。


$ ./arraynew.rb

[[], [nil, nil, nil], [“coin”, “coin”, “coin”, “coin”, “coin”, “coin”],

[11], [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196]]


我们可以所有创建的数组。




接下来的脚本展示了多种创建数组的方法。


#!/usr/bin/ruby

integers = [1, 2, 3, 4, 5]
animals = %w( donkey dog cat dolphin eagle )
weights = Array.new
weights << 4.55 << 3.22 << 3.55 << 8.55 << 3.23

puts integers.inspect
puts animals.inspect
puts weights.inspect

我们创建了三个数组分别包含了整数、字符串和小数。


integers = [1, 2, 3, 4, 5]

这行创建了一个包含5个整数的数组。这是最经典的方法。数组的每个元素放在中括号中用逗号隔开。


animals = %w( donkey dog cat dolphin eagle )

这行代码创建了一个有5个元素的字符串数组。这种方式我们不需要逗号和双引号,减少了按键次数。


weights = Array.new
weights << 4.55 << 3.22 << 3.55 << 8.55 << 3.23

第三种方法有两步。首先创建一个数组对象,然后用数据将其初始化。这是正式的数组创建方法。上面方法实际上是这个的简写。


puts integers.inspect

The inspect method prints the string representation of the array to the terminal.


$ ./creation.rb

[1, 2, 3, 4, 5]

[“donkey”, “dog”, “cat”, “dolphin”, “eagle”]

[4.55, 3.22, 3.55, 8.55, 3.23]


例子的输出结果。




数组的数据项没限制必须是数字和字符串。数组可以包含Ruby中的所有类型的数据。


#!/usr/bin/ruby

class Empty

end

nums = [1, 2, 3, 4, 5]

various = [1, -1, “big”, 3.4, Empty.new, nums, :two]

puts various.inspect

我们在数组中放了多种Ruby对象。


various = [1, -1, “big”, 3.4, Empty.new, nums, :two]

这个数组包含了数字、字符串、自定义对象、另一个数组和一个符号。


$ ./arrayobjects.rb
[1, -1, “big”, 3.4, #<Empty:0x987f704>, [1, 2, 3, 4, 5], :two]

arrayobjects.rb脚本的运行结果。




下一个例子展示嵌套数组;即一个数组包含另一个数组。Ruby中可以在数组中嵌套数组。


#!/usr/bin/ruby

numbers = [1, 2, 3, [2, 4, 6, [11, 12]]]

puts numbers.length
puts numbers[0], numbers[1]

puts numbers[3][0]
puts numbers[3][1]

puts numbers[3][3][0]
puts numbers[3][3][1]

puts numbers.flatten!.inspect

数组[11, 12]被嵌套在[2, 4, 6, …]里,这个数组又被嵌套在[1, 2, 3, …]数组里。


puts numbers.length

length方法返回4。内嵌数组只算一个元素。


puts numbers[0], numbers[1]

这里[]字符用于访问数组的元素。上面这行代码返回第一个和第二个元素。


puts numbers[3][0]
puts numbers[3][1]

这里我们访问内嵌数组的元素。[3][0]返回内嵌数组的第一个元素,在这里是2。同样的[3][1]返回内嵌数组的第二个元素,这里是4。


puts numbers[3][3][0]
puts numbers[3][3][1]

现在我们进入更深一层。我们访问更深一层数组的元素。[3][3]返回[11, 12]数组。然后从这个数组获取第一个和第二个元素。


puts numbers.flatten!.inspect

flatten!方法将数组变为平坦。它将所有的内嵌数组元素创建成一个新的数组。


$ ./arrayofarrays.rb

4

1

2

2

4

11

12

[1, 2, 3, 2, 4, 6, 11, 12]


输出结果。


打印数组内容


要在终端上打印数组的元素有多种方法可以完成。


#!/usr/bin/ruby

integers = [1, 2, 3, 4, 5]

puts integers
puts integers.inspect

integers.each do |e|
puts e
end

这个脚本我们将数组的元素打印了三次。


puts integers

数组作为puts/print方法的参数是最简单的方式。每行将打印一个元素。


puts integers.inspect

使用inspect方法输出结果的可读性更好。


integers.each do |e|
puts e
end

each方法为每个元素都调用一次一个代码块,元素作为参数传递。我们简单的参元素使用puts方法。


$ ./printarray1.rb

1

2

3

4

5

[1, 2, 3, 4, 5]

1

2

3

4

5


数组在终端上打印了三次。




第二个例子我们提供了两个额外的方法打印数组。


#!/usr/bin/ruby

integers = [1, 2, 3, 4, 5]

integers.length.times do |idx|
puts integers[idx]
end

integers.each_with_index do |num, idx|
puts “value #{num} has index #{idx}”
end

第一种情况我们组合使用了lengthtimes方法。第二种情况我们使用了each_with_index方法。


integers.length.times do |idx|
puts integers[idx]
end

length方法返回数组的长度。times方法将接下来的代码块迭代length次,传递的值从0到length-1。这些数字作为数组的索引使用。


integers.each_with_index do |num, idx|
puts “value #{num} has index #{idx}”
end

each_with_index方法迭代数组并将元素及其索引传递给代码块。用这种方法我们可以简单的打印元素和它的索引。


$ ./printarray2.rb

1

2

3

4

5

value 1 has index 0

value 2 has index 1

value 3 has index 2

value 4 has index 3

value 5 has index 4


输出结果。


读取数组元素


这节我们将从数组读取数据。


#!/usr/bin/ruby

lts = %w{ a b c d e f g h}

puts lts.first
puts lts.last
puts lts.at(3)

第一个例子我们展示了三个简单的方法进行数据检索。


puts lts.first
puts lts.last

first方法读取数组的第一个元素;last方法读取数组的最后一个元素。


puts lts.at(3)

at方法返回指定索引的元素。


$ ./retrieval.rb

a

h

d


retrieval.rb程序的输出结果。




[]符号可以用于访问数据。这是传统访问数据的方法,许多语言都使用这种方法。


#!/usr/bin/ruby

lts = %w{ a b c d e f g h }

puts lts[0]
puts lts[-1]
puts lts[0, 3].inspect
puts lts[2..6].inspect
puts lts[2…6].inspect

我们展示了5个使用[]符号的例子。


puts lts[0]
puts lts[-1]

我们获取数组的第一个和最后一个元素。第一项的索引为0,最后一项的索引为-1。


puts lts[0, 3].inspect

当中括号里有两个数时,第一个是开始的索引,第二个是长度。这行代码返回从0开始的3个元素。注意inspect方法仅是为了让输出可读而已。


puts lts[2..6].inspect
puts lts[2…6].inspect

我们可以在中括号使用范围操作符。第一行读取第2个到第6个元素,第二行读取第2个到第5个元素。




接下来展示values_at方法。这个方法的优势是可以选择多个位置的元素。


#!/usr/bin/ruby

lts = %w{ a b c d e f g h}

puts lts.values_at(1..5).inspect
puts lts.values_at(1, 3, 5).inspect
puts lts.values_at(1, 3, 5, 6, 8).inspect
puts lts.values_at(-1, -3).inspect

values_at method方法返回一个数组包含选中的元素。inspect方法仅是为了让输出可读而已。


puts lts.values_at(1..5).inspect

这行代码返回索引为1到5的元素。


puts lts.values_at(1, 3, 5).inspect

这里我们读取索引为1、3、5的元素。


puts lts.values_at(1, 3, 5, 6, 8).inspect

我们可以指定多个索引,如果指定的索引没有元素则返回nil


puts lts.values_at(-1, -3).inspect

负数的索引表示从数组尾部开始。


$ ./retrieval3.rb

[“b”, “c”, “d”, “e”, “f”]

[“b”, “d”, “f”]

[“b”, “d”, “f”, “g”, nil]

[“h”, “f”]


脚本的输出结果。




我们使用fetch方法从数组读取数据。


#!/usr/bin/ruby

lts = [0, 1, 2, 3, 4, 5, 6]

puts lts.fetch(0)
puts lts.fetch(-2)
puts lts.fetch(8, ‘undefined’)
puts lts.fetch(8) { |e| -2e }

我们展示了fetch方法的一些形式的用法。


puts lts.fetch(0)
puts lts.fetch(-2)

第一行打印了数组的第一个元素。第二行打印了倒数第二个元素。


puts lts.fetch(8, ‘undefined’)

第三种形式是返回指定索引的元素,如果索引超出范围则返回默认值,这里是‘undefined’。没有第二个参数则会抛出IndexError错误。


puts lts.fetch(8) { |e| -2e}

最后一种形式我们定义了一个代码块,传递了索引对应的值,这个方法返回了代码块调用的结果。


$ ./retrieval4.rb

0

5

undefined

-16


脚本的输出结果。




我们将展示taketake_while方法的用法。


#!/usr/bin/ruby

lts = %w{ a b c d e f g h}


puts lts.take(4).inspect

lts2 = lts.take_while { |e| e < ‘f’ }
puts lts2.inspect

take n方法返回开头的n个元素。take_while方法将元素传递给一个代码块,直到代码块返回nil或者false才停止迭代,并返回之前的元素。


puts lts.take(4).inspect

这里我们返回开头的4个元素。


lts2 = lts.take_while { |e| e < ‘f’ }
puts lts2.inspect

这里我们从源数组创建了一个新的数组,这个新数组包含了所有小于’f’的字符。


$ ./retrieval5.rb

[“a”, “b”, “c”, “d”]

[“a”, “b”, “c”, “d”, “e”]


retrieval5.rb程序的输出结果。




slice方法与[]符号相同。返回一个或者多个元素。


#!/usr/bin/ruby

lts = %w{ a b c d e f g h}

puts lts.slice(0)
puts lts.slice(-1)
puts lts.slice(0, 3).inspect
puts lts.slice(2..6).inspect
puts lts.slice(2…6).inspect

展示了5个slice 方法的例子。


puts lts.slice(0)
puts lts.slice(-1)

这些形式的slice方法返回一个数组元素。第行代码返回第一个元素,第二行代码返回最后一个元素。


puts lts.slice(0, 3).inspect

第一个参数是起始索引,第二个参数是长度。这一行代码返回从0开始的3个元素。


puts lts.slice(2..6).inspect
puts lts.slice(2…6).inspect

我们可以在slice方法中使用范围操作符。第一行读取第2到6的元素,第二行读取第2到5的元素。


$ ./retrieval6.rb

a

h

[“a”, “b”, “c”]

[“c”, “d”, “e”, “f”, “g”]

[“c”, “d”, “e”, “f”]


slice方法返回数组的一部分,一个或者多个元素。




可以随机选择数组的一个元素。Ruby中的sample方法可以实现。


#!/usr/bin/ruby

lts = %w{ a b c d e f g h}

puts lts.sample
puts lts.sample(3).inspect

sample方法有两种形式。第一种我们选择一个随机元素。第二种我们选择n个随机元素。


$ ./random.rb

b

[“c”, “f”, “d”]

$ ./random.rb

d

[“c”, “d”, “e”]


执行两次结果不同。


使用数组


接下来的例子介绍一些Ruby数组的方法。


#!/usr/bin/ruby

num1 = [1, 2, 3, 4, 5]
num2 = [6, 7, 8, 9, 10]

puts num1 + num2
puts num1.concat num2

我们定义了两个数组,将它们相加。


puts num1 + num2
puts num1.concat num2

有两个数组相加的方法。使用+操作符或者concat方法。




Ruby中数组有丰富的方法。例如length方法返回数组的元素个数。


#!/usr/bin/ruby

lts = %w{ a b c d e f}

puts lts.inspect
puts “Array has #{lts.length} elements”
puts “The first element is #{lts.first}”
puts “The last element is #{lts.last}”

puts lts.eql? lts.dup
puts lts.eql? lts.dup.delete_at(0)

lts.clear
puts lts.inspect
puts lts.empty?

上面的脚本我们介绍了7个新方法。


puts “Array has #{lts.length} elements”

length方法决定数组的大小。


puts “The first element is #{lts.first}”
puts “The last element is #{lts.last}”

这里我们获取第一个和最后一个元素。


puts lts.eql? lts.dup

eql?方法指出两个数组是否相等。这里是返回truedup方法创建一个浅复制对象。


puts lts.eql? lts.dup.delete_at(0)

delete_at方法从数组的开头删除元素。现在两个数组不相同了。


lts.clear

clear方法删除数组的所有元素。


puts lts.empty?

empty?方法检查数组是否为空。这里返回true,因为我们已经将所有元素都删除了。


$ ./basics.rb

[“a”, “b”, “c”, “d”, “e”, “f”]

Array has 6 elements

The first element is a

The last element is f

true

false

[]

true


输出结果。




一些Ruby的数组方法以感叹号结尾。这是Ruby的习惯。感叹号告诉程序员这个方法会修改数据。感叹号本身没有什么作用,它只是名字的约定。


#!/usr/bin/ruby

chars = %w{a b c d e}

reversed_chars = chars.reverse
puts reversed_chars.inspect
puts chars.inspect

reversed_chars = chars.reverse!
puts reversed_chars.inspect
puts chars.inspect

Ruby中有一些比较相似的方法。reversereverse!方法都是改为数组元素的顺序,将它们反转。不同在于reverse方法返回反转后的数组,原数组不变。reverse!方法会同时修改原数组。


$ ./twotypes.rb

[“e”, “d”, “c”, “b”, “a”]

[“a”, “b”, “c”, “d”, “e”]

[“e”, “d”, “c”, “b”, “a”]

[“e”, “d”, “c”, “b”, “a”]


我们清晰的看到前两个数组不同,第三和第四个数组是相同的。




一些其他的方法展示。


#!/usr/bin/ruby

numbers = [1, 2, 2, 2, 3, 4, 5, 8, 11]

puts numbers.index 2
puts numbers.index 11
puts numbers.rindex 2

puts numbers.include? 3
puts numbers.include? 10

puts numbers.join ‘-‘
puts numbers.uniq!.inspect

介绍5个额外的方法。


puts numbers.index 2
puts numbers.index 11

index方法返回数组中元素对应的索引。索引是从左算起。第一行返回1,它是第一个2的索引。数组中仅有一个11,它的索引是8。


puts numbers.rindex 2

rindex方法返回从右边开始的索引。这里2的最右索引为3。


puts numbers.include? 3
puts numbers.include? 10

include?方法检查一个元素是否在数组中。第一行返回true,3在数组中。第二行返回false,数组中没有10。作为约定Ruby中以问号结尾的方法返回一个布尔值,并且对数组没有影响。


puts numbers.join ‘-‘

join方法返回一个字符串,它是将数组的元素用指定的符号分隔开来。


puts numbers.uniq!.inspect

uniq!方法移除重复的元素。在数组中有3个2,调用方法之后就只剩一个2了。


$ ./methods2.rb

1

8

3

true

false

1-2-2-2-3-4-5-8-11

[1, 2, 3, 4, 5, 8, 11]


注意join方法产生的是一个字符串,它是数组的数字用-符号分隔。


修改数组


这一节我们介绍与数组修改相关的方法。基本的我们做一些插入和删除操作。


#!/usr/bin/ruby

lts = []

lts.insert 0, ‘E’, ‘F’, ‘G’
lts.push ‘H’
lts.push ‘I’, ‘J’, ‘K’
lts << ‘L’ << ‘M’
lts.unshift ‘A’, ‘B’, ‘C’
lts.insert(3, ‘D’)

puts lts.inspect

从一个空数组开始,我们使用不同的插入方法。


lts.insert 0, ‘E’, ‘F’, ‘G’

insert方法往lts数组插入了3个元素。


lts.push ‘H’
lts.push ‘I’, ‘J’, ‘K’

push方法往数组添加元素,我们可以添加一个或多个元素。


lts << ‘L’ << ‘M’

<<push方法相同。这个操作可以链式调用。


lts.unshift ‘A’, ‘B’, ‘C’

unshift方法将元素插入在数组前端。


lts.insert(3, ‘D’)

这里insert方法在指定的位置插入’D’字符。


$ ./insertion.rb

[“A”, “B”, “C”, “D”, “E”, “F”, “G”, “H”, “I”, “J”, “K”, “L”, “M”]


使用上面的方法,我们创建了一个包含大写字母的数组。




一些删除数组元素的方法。


#!/usr/bin/ruby

lts = %w{ a b c d e f g h}

lts.pop
lts.pop

puts lts.inspect

lts.shift
lts.shift

puts lts.inspect

lts.delete_at(0)
lts.delete(‘d’)

puts lts.inspect

puts lts.clear
puts lts.inspect

这个脚本展示了5个用于删除数组元素的方法。


lts = %w{ a b c d e f g h}

创建一个有8个元素的数组。


lts.pop

pop方法移除最后一个元素。


lts.shift

shift方法移除数组的第一个元素。


lts.delete_at(0)

delete_at方法删除指定位置的元素。我们删除剩余元素的第一个元素。


puts lts.clear

clear方法清除所有元素。


lts.delete(‘d’)

delete方法删除指定的一项数据。


$ ./deletion.rb

[“a”, “b”, “c”, “d”, “e”, “f”]

[“c”, “d”, “e”, “f”]

[“e”, “f”]

[]


输出结果。




目前为止我们使用的方法同时只修改一个数组项。Ruby中有一些方法可以一次修改多个数组项的。


#!/usr/bin/ruby

nms = [2, -1, -4, 0, 4, 3, -2, 3, 5]

nms.delete_if { |x| x < 0 }

puts nms.inspect

例子介绍了delete_if方法用于删除所有符合条件式的数据项。


nms.delete_if { |x| x < 0 }

这行删除所有的负数。


$ ./delete_if.rb

[2, 0, 4, 3, 3, 5]


我们从nms数组中删除了所有的负数。




我们展示两个其他的方法处理多数据项。


#!/usr/bin/ruby

lts = %w{ a b c d e f g}

puts lts.inspect

lts.reject! do |e|
e =~ /[c language=”-y”][/c]/
end

puts lts.inspect

lts.replace([“x”, “y”, “z”])
puts lts.inspect

我们使用了两个方法,reject!replace


lts.reject! do |e|
e =~ /[c language=”-y”][/c]/
end

reject!方法移除了所有项满足条件式的数据。这里我们删除所有符合正则式的字母。


lts.replace([“x”, “y”, “z”])

replace方法将使用给定的数据替换数组项。如果可能它会截断或者扩展数组。


$ ./modify.rb

[“a”, “b”, “c”, “d”, “e”, “f”, “g”]

[“a”, “b”]

[“x”, “y”, “z”]


modify.rb例子的输出结果。


集合操作


这一节中我们展示Ruby数组的集合操作。数学中集合是独立对象的收集。


#!/usr/bin/ruby

A = [1, 2, 3, 4, 5]
B = [4, 5, 6, 7, 8]

union = A | B
isect = A & B
diff1 = A - B
diff2 = B - A
sdiff = (A - B) | (B - A)

puts “Union of arrays: #{union}”
puts “Intersection of arrays: #{isect}”
puts “Difference of arrays A - B: #{diff1}”
puts “Difference of arrays B - A: #{diff2}”
puts “Symmetric difference of arrays: #{sdiff}”

上面的脚本我们展示了一些集合操作,并集、交集、差集和对称差集。


nums1 = [1, 2, 3, 4, 5]
nums2 = [4, 5, 6, 7, 8]

字义了两个整数数组。都是集合,因此每个元素都只出现了一次。两个数组有两个相同的数,4和5。


union = nums1 | nums2

数组的并集。两个数组相加,每个元素最终也只出现一次。


isect = A & B

数组交集。输出两个数组都存在的元素。这里是4和5。


diff1  = A - B
diff2 = B - A

两个差集,也称补集。第一行我们得到了所有在A中出现B中没有出现的元素。第二行我们得到B中出现A中没有出现的元素。


sdiff = (A - B) | (B - A)

对称差集。A或B中存在,但不同时存在于A和B。


$ ./setoperations.rb

Union of arrays: [1, 2, 3, 4, 5, 6, 7, 8]

Intersection of arrays: [4, 5]

Difference of arrays A - B: [1, 2, 3]

Difference of arrays B - A: [6, 7, 8]

Symmetric difference of arrays: [1, 2, 3, 6, 7, 8]


输出结果。


select、collect、map方法


下面的例子我们展示三个方法:selectcollectmap


#!/usr/bin/ruby

nums = [1, 3, 2, 6, 7, 12, 8, 15]

selected = nums.select do |e|
e > 10
end

puts selected.inspect

collected = nums.collect do |e|
e < 10
end

puts collected.inspect

mapped = nums.map do |e|
e2
end

puts mapped.inspect

所有这些方法都对数组的元素执行许多操作。


selected = nums.select do |e|
e > 10
end

上面的代码使用collect方法创建了一个新数组。我们只选择了满足条件式的元素。这里我们选择了所有大于10的元素。


collected = nums.collect do |e|
e < 10
end

collect方法稍微不同。它只收集代码块的返回值。新的数组只包含true和false。


mapped = nums.map do |e|
e
2
end

map方法与collect方法相同。上面的代码根据已存在的数组创建了一个新的数组。每个元素都乘以2.


$ ./mass.rb

[12, 15]

[true, true, true, true, true, false, true, false]

[2, 6, 4, 12, 14, 24, 16, 30]


创建了新数组。


元素排序


最后我们对数组元素进行排序。


#!/usr/bin/ruby

planets = %w{ Mercury Venus Earth Mars Jupiter
Saturn Uranus Neptune Pluto }

puts “#{planets.sort}”
puts “#{planets.reverse}”
puts “#{planets.shuffle}”

例子中使用了三个Ruby数组的方法对数组元素进行重组。


puts “#{planets.sort}”

sort方法按字母顺序进行排序。


puts “#{planets.reverse}”

reverse方法反转元素并返回新的数组。


puts “#{planets.shuffle}”

shuffle方法将数组元素随机重组。


$ ./ordering.rb

[“Earth”, “Jupiter”, “Mars”, “Mercury”, “Neptune”, “Pluto”, “Saturn”, …]

[“Pluto”, “Neptune”, “Uranus”, “Saturn”, “Jupiter”, “Mars”, “Earth”, …]

[“Earth”, “Jupiter”, “Mercury”, “Saturn”, “Mars”, “Venus”, “Uranus”, …]


例子的输出结果。


在这章我们学习了Ruby的数组。




原文地址: http://zetcode.com/lang/rubytutorial/arrays/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程9——控制流

控制流

这章的教程我们将讨论控制流。


条件和循环改变了Ruby程序的流程。条件式是执行条件语句下面的特定语句。循环是执行多次的代码块。程序开始后,语句从源文件顶部到底部一行一行的执行。


if语句


if关键字用于检查一个表达式是否为真。如果为真则执行语句。这个语句可以是单一的语句,也可以是复合语句。复合语句是由多条语句构成的闭合代码块。代码块用end关键字闭合,then关键字是可选的。


#!/usr/bin/ruby

num = gets.to_i

if num > 0 then

puts “num variable is positive”
puts “num variable equals to #{num}”
end

输入一个数字,如果这个数字大于0则打印两条消息,否则什么也不做。


$ ./simpleif.rb

4

num variable is positive

num variable equals to 4


条件为真并且消息在终端上打印了。




我们可以使用else关键字不创建一个简单的分支。如果if关键字后的表达式求值为假,那么else后面的语句将自动执行。这些代码使用end关键字闭合起来。


#!/usr/bin/ruby

age = 17

if age > 18

puts “Driving licence issued”
else

puts “Driving licence not permitted”
end

这里创建了一个age变量,布尔表达式求值为假,因此在终端上打印”Driving licence not permitted”。


$ ./licence.rb

Driving licence not permitted


我们可以使用elsif关键字创建多个分支。仅当前一个条件不为真时elsif关键字才会测试另一个条件。注意我们可以使用多个elsif关键字。


#!/usr/bin/ruby

print “Enter a number: “

num = gets.to_i

if num < 0

puts “#{num} is negative”
elsif num == 0

puts “#{num} is zero”
elsif num > 0

puts “#{num} is positive”
end

创建一个数字变量判断它是正数、负数还是0。根据输入值的不同将打印不同的消息。


case语句


case语句是一个程序控制流语句。它允许一个变量或者表达式的值控制程序的执行流程。相比ifelsif它创建多分支更简单。


我们创建一个变量或者表达式。case关键字用于针对一个列表的值测试这个变量或者表达式的值。这个列表的值是使用when关键字呈现出来。如果值匹配,那么when关键字后面的语句将执行。有一个可选的else语句,如果没有匹配的它将执行。


#!/usr/bin/ruby

print “Enter top level domain: “

domain = gets.chomp

case domain
when “us”
puts “United States”
when “de”
puts “Germany”
when “sk”
puts “Slovakia”
when “hu”
puts “Hungary”
else
puts “Unknown”
end

这个程序中我们创建了一个domain变量,它的值从命令行读取。使用when语句测试这个变量的值。这有些选项。如果值等于”us”则打印”United States”字符串。


domain = gets.chomp

我们使用gets方法获取一个输入值。使用chomp方法去掉换行符。


$ ./domains.rb

Enter top level domain: hu

Hungary


我们输入”hu”,程序返回”Hungary”。


while、until语句


while语句是一个控制流语句允许代码多次执行。当的条件为真时代码将执行。


while关键字执行使用end闭合的代码块语句。每次表达式为真时这些语句将执行。


#!/usr/bin/ruby

i = 0
sum = 0

while i < 10 do
i = i + 1
sum = sum + i
end

puts “The sum of 0..9 values is #{sum}”

这个例子计算了一个范围数的和。


while循环有三部分:初始化、测试和更新。每次执行语句称为周期。


i = 0
sum = 0

初始化变量isumi用于计数。


while i < 10  do

end

whiledo之间的表达式是第二阶段,测试。注意do关键字是可选的。,内部的语句将会执行直到表达式的值为假。


i = i + 1

这是循环的第三阶段——更新。我们增加计数值。注意不当的处理会导致死循环。


$ ./while.rb

The sum of 0..9 values is 55


This is the output of the example.




当条件为假时until控制流语句将执行。当条件为真时循环停止。


#!/usr/bin/ruby

hours_left = 12

until hours_left == 0

if hours_left == 1
puts “There is #{hours_left} hour left”
else
puts “There are #{hours_left} hours left”
end

hours_left -= 1
end

这个例子中创建了一个变量hours_left。我们开始计数,每次循环打印还有几个小时。当变量值等于0时循环停止。


$ ./until.rb

There are 12 hours left

There are 11 hours left

There are 10 hours left

There are 9 hours left

There are 8 hours left

There are 7 hours left

There are 6 hours left

There are 5 hours left

There are 4 hours left

There are 3 hours left

There are 2 hours left

There is 1 hour left


程序运行的结果。


for语句


当循环次数是已知时,我们可以使用for语句。for循环使用in接着一个范围。对这个范围的每个元素都执行代码块的语句。这些语句使用end关键字闭合。do关键字是可选的。


#!/usr/bin/ruby

for i in 0..9 do

puts “#{i}”
end

这个例子中我们打印了0到9的数。每次循环i变量保存了这个范围的一个数,这个数将在终端上打印。


$ ./forloop.rb

0

1

2

3

4

5

6

7

8

9


例子运行结果。




要遍历一个数组的元素可以使用数组的length方法。


#!/usr/bin/ruby

planets = [“Mercury”, “Venus”, “Earth”, “Mars”, “Jupiter”,
“Saturn”, “Uranus”, “Neptune”]

for i in 0…planets.length

puts planets[i]
end

这个例子中我们创建了一个数组planets,我们遍历这个数组打印这个数组的每个元素。


planets = [“Mercury”, “Venus”, “Earth”, “Mars”, “Jupiter”,
“Saturn”, “Uranus”, “Neptune”]

planets数组。


for i in 0…planets.length

length方法返回数组的长度。数组从0开始,最后一个索引是n-1。


puts planets[i]

打印数组指定的一个元素。


$ ./planets2.rb

Mercury

Venus

Earth

Mars

Jupiter

Saturn

Uranus

Neptune


程序的运行结果。


each方法


在Ruby中我们可以使用each方法遍历数组的每个元素。它接受两个参数。一个元素和一个代码块。元素放在管道之间,它保存了当前循环的数据项。代码块是每次要执行的内容。


#!/usr/bin/ruby

planets = [“Mercury”, “Venus”, “Earth”, “Mars”, “Jupiter”,
“Saturn”, “Uranus”, “Neptune”]

planets.each do |planet|

puts planet
end

这个例子我们使用each迭代器遍历planets数组。


planets.each do |planet|

puts planet
end

each迭代器是planets数组的一个方法。planet保存了此次迭代的数据项。我们可以使用任何我们想使用的字符。我们可以使用{}代替doend关键字。


break、next语句


break语句用于终止whilefor或者case的代码块语句。


#!/usr/bin/ruby

while true

r = 1 + rand(30)
print “#{r} “

if r == 22
break
end
end

puts

我们定义了一个无限循环,使用break语句退出这个循环。我们选择1到30的数,打印它。如果等于22则结束while循环。


while true

end

这是一个无限循环,while的条件总是为真。退出无限循环的唯一方法是使用break


r = 1 + rand(30)
print “#{r} “

计算从1到30的随机数,并将其打印。


if r == 22
break
end

如果数等于22,结束循环。


$ ./break.rb

20 14 6 26 30 12 2 10 18 29 28 11 30 26 20 22


可能的输出结果。




next语句用于跳过此次循环,继续下次循环。它可与forwhile联合使用。


接下来的例子我们打印不能被2整除的数一列表。


#!/usr/bin/ruby

num = 0

while num < 100

num += 1

if (num % 2 == 0)
next
end

print “#{num} “
end

puts

我们使用while循环迭代1到99的数。


if (num % 2 == 0)
next
end

如果这个数可以被2整除,next语句将执行,循环的其余将跳过。


$ ./next.rb

1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39

41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77

79 81 83 85 87 89 91 93 95 97 99


程序的输出结果。


redo语句


redo语句重新开始循环而不检查循环的条件式。下面的例子有点复杂,它展示了redo语句和其它的一些特性。


#!/usr/bin/ruby

options = [“rock”, “scissors”, “paper”]

while true

print <<TEXT
1 - rock
2 - scissors
3 - paper
9 - end game
TEXT

val = gets.to_i

r = rand(3) + 1

if val == 9
puts “End”
exit
end

if ![1, 2, 3, 9].include?(val)
puts “Invalid option”
redo
end

computer = options[r-1]
human = options[val-1]

puts “I have #{computer}, you have #{human}”

if val == r
puts “Tie, next throw”
redo
end


if val == 1 and r == 2
puts “Rock blunts scissors, you win”

elsif val == 2 and r == 1
puts “Rock blunts scissors, you loose”

elsif val == 2 and r == 3
puts “Scissors cut paper, you win”

elsif val == 3 and r == 2
puts “Scissors cut paper, you loose”

elsif val == 3 and r == 1
puts “Paper covers rock, you win”

elsif val == 1 and r == 3
puts “Paper covers rock, you loose”

end
end

这是一个简单的Rock-paper-scissors游戏。这个例子中我们用到了redo语句、条件式、随机数、数组和输入。


options = [“rock”, “scissors”, “paper”]

定义了游戏可能用到的选项数组。这三个词在打印消息时会用到。


    print <<TEXT
1 - rock
2 - scissors
3 - paper
9 - end game
TEXT

使用定界符在终端上打印一个菜单。菜单在每次游戏循环时都打印。


val = gets.to_i

r = rand(3) + 1

这几行代码从终端输入一个值,然后选择一个1、2、3的随机数。


if val == 9
puts “End”
exit
end

如果输入为9,打印’End’并退出程序。


if ![1, 2, 3, 9].include?(val)
puts “Invalid option”
redo
end

如果用户选择了一个不是菜单的值,我们提示无效选项并重新循环。


computer = options[r-1]
human = options[val-1]

puts “I have #{computer}, you have #{human}”

将数字转化为字符串,并同时打印用户的选择和计算机的选择。


if val == r
puts “Tie, next throw”
redo
end

如果选择相同则是平局,开始新的游戏循环。


if val == 1 and r == 2
puts “Rock blunts scissors, you win”

elsif val == 2 and r == 1
puts “Rock blunts scissors, you loose”


使用多个ifelsif分支,比较用户和计算机的选择以决定谁是赢家。


$ ./redo.rb

1 - rock

2 - scissors

3 - paper

9 - end game

3

I have paper, you have paper

Tie, next throw

1 - rock

2 - scissors

3 - paper

9 - end game

2

I have rock, you have scissors

Rock blunts scissors, you loose

1 - rock

2 - scissors

3 - paper

9 - end game

1

I have scissors, you have rock

Rock blunts scissors, you win

1 - rock

2 - scissors

3 - paper

9 - end game

9

End


程序输出结果。


这部分的Ruby教程我们谈论了结构控制流。




原文地址: http://zetcode.com/lang/rubytutorial/flowcontrol/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程8——表达式

表达式

在这章的教程将介绍Ruby的表达式。


表达式是由操作数和操作符构成的。表达式的操作符表明了对操作数应用什么操作。表达式中操作符的求值顺序由操作符的优先级和结合律决定的。


操作符是一个特殊的符号表明了要执行的确切操作。编程语言中的操作符取自数学运算。程序员使用数据,操作符用于处理数据。操作数是操作符的参数。


以下表格中按优先级展示Ruby的操作符(高优先级在前):



















































































分类 符号
解析,访问操作符 :: .
数组操作符 [] []=
求幂
非,反,一元加、减 ! ~ + -
乘、除、模 / %
加、减 + -
移位 << >>
位与 &
位或、逻辑或 ^ |
关系运算 > >= < <=
相等、模式匹配 <=> == === != =~ !~
逻辑与 &&
逻辑或 ||
范围操作符 .. …
三目运算 ?:
分配操作 = += -= = = /= %= &= |= ^= <<= >>= ||= &&=
否定 not
逻辑或、与 or and


同一行的操作符具有相同的优先级。


一个操作符通常有两个操作数。那些只有一个操作数的操作符称为一元操作符。有两个操作数的称为二元操作符。有一个三元操作符(?:)有三个操作数。


操作符可能用于不同的上下文。例如+操作符,从上面的表格可知它能用于不同情况。数字求和、连接字符串、作为数字的符号。我们称这个操作符被重载了。


正负号操作符


有两个正负号操作符,+和-,用于指定或者修改值的符号。


#!/usr/bin/ruby

puts +2
puts -2

+和-指明了值的符号。加号表明是正数,通常可省略。




接下来的例子我们使用减号。


#!/usr/bin/ruby

a = 1

puts a
puts -(a)
puts -(-(a))

减号改变了值的符号。


$ ./sign.rb

1

-1

1


输出结果。


分配操作符


分配操作符=将一个值分配给一个变量。变量是这个值的点位符。数学运算中=操作符有不同的意义。在方程式中=是相等操作,左边的值等于右边的值。


x = 1
puts x # prints 1

这里分配的一个数字给x变量。


x = x + 1
puts x # prints 2

前一个表达式中数学运算中是行不通的。但是在程序中是合法的。这个表达式是将x变量加1,右边等于2,并把2赋值给x。


3 = x;

这个语法是错误的,我们不能组值分配字面符。


解析,成员访问操作符


有两个操作符具有最高的优先级,这意味着它们总是先求解。


#!/usr/bin/ruby

class MyMath
Pi = 3.1415926535
end

module People
Name = “People”
end

puts MyMath::Pi
puts People::Name

第一个例子展示了::命名空间解析操作符。它可以访问一个定义在其它类或模块里的常量、模块或者类。它用于提供命名空间,这样方法和类的名字就不会与其他作者的类冲突了。


class MyMath
Pi = 3.1415926535
end

module People
Name = “People”
end

这里创建了一个模块的一个类,分别定义了一个常量。


puts MyMath::Pi
puts People::Name

我们使用::操作符访问它们的常量。


$ ./resolution.rb

3.1415926535

People


resolution.rb脚本的输出结果。




点(.)操作符是成员访问,用于调用对象的方法。


#!/usr/bin/ruby

class Person

def initialize name, age
@name = name
@age = age
end

def info
“#{@name} is #{@age} years old”
end

end

p = Person.new “Jane”, 17
puts p.info

puts “ZetCode”.reverse

这个例子中我们创建了两个对象,一个是用户定义的,一个是预定义的。我们对这些对象使用点操作符。


p = Person.new “Jane”, 17
puts p.info

在这行使用点操作符调用了两个方法:new和info。


puts “ZetCode”.reverse

字符串是内建对象,具有一个reverse方法。


$ ./memberaccess.rb

Jane is 17 years old

edoCteZ


输出结果。


字符串连接


Ruby中+操作符同样可以用于字符串连接。在不同上下文中操作符作用不同,我们称这个为重载。


#!/usr/bin/ruby

puts “Return “ + “of “ + “the “ + “King”
puts “Return “.+”of “.+ “the “.+”King”

我们将三个字符串连接在一起。


puts “Return “ + “of “ + “the “ + “King”

我们使用+操作符连接字符串。


puts “Return “.+”of “.+ “the “.+”King”

+操作符也是Ruby的一个方法。我们可以使用访问操作符(.)来调用它。


$ ./catstrings.rb

Return of the King

Return of the King


catstrings.rb程序的运行结果。


增、减操作符


Ruby中没有如下操作。

x++;



y–;


这些是C语言的增、减操作。


如果你熟悉Java、C、C++,你可能了解这些操作符。但是这些在Ruby中无效的。同样在Python中也没有。


算术操作符


下面的表格是Ruby中的算术操作符。



































符号名字
+
-
/
%
**

下面的例子中我们使用了算术操作符。


#!/usr/bin/ruby

a = 10
b = 11
c = 12

puts a + b + c
puts c - a
puts a b
puts c / 3
puts c % a
puts c a

在前面的例子我们使用了加、减、乘、除和取余操作。这些与数学上的是相同的。


puts c % a

%操作符是取余或者求模。


$ ./arithmetic.rb

33

2

110

4

2

61917364224


输出结果。




接下来展示整数和浮点数除法的区别。


#!/usr/bin/ruby

puts 5 / 2

puts 5 / 2.0
puts 5.0 / 2
puts 5.to_f / 2

上面的例子我们将两个数相除。


puts 5 / 2

这个表达式中的操作数都是整数,我们进行的是整数相除。结果返回的也是整数。


puts 5 / 2.0
puts 5.0 / 2
puts 5.to_f / 2

如果其中一个或两个是浮点数,我们进行的是浮点数相除。


$ ./division.rb

2

2.5

2.5

2.5


division.rb程序的运行结果。




Ruby有其他方式进行除运算。这些都是有效的方法调用。


#!/usr/bin/ruby

puts 5.div 2.0
puts 5.fdiv 2
puts 5.quo 2
puts 5.0.quo 2.0

上面例子中我们使用了divfdivquo方法。


puts 5.div 2.0

div总是进行整数除,即使操作数是浮点数。


puts 5.fdiv 2

fdiv总是进行浮点数除法。


puts 5.quo 2
puts 5.0.quo 2.0

quo方法进行精确的除法。如果操作数是浮点数则返回浮点数,否则是有理数。


$ ./otherdivision.rb

2

2.5

5/2

2.5


布尔操作符


Ruby中有以下布尔操作符。























符号 名字
&& 逻辑与
|| 逻辑或
!


布尔操作符处理真假值。Ruby中有额外的布尔操作符,andor&not。它们操作是相同的,只是优先级低一些。与Perl语言不同,这需要一个低优先级的布尔操作符。


#!/usr/bin/ruby

x = 3
y = 8

puts x == y
puts y > x

if y > x then
puts “y is greater than x”
end

许多表达式的结果都是一个布尔值。布尔值用于条件语句。


puts x == y
puts y > x

返回布尔值的相关操作符。这两行打印为false和true。


if y > x then
puts “y is greater than x”
end

仅当月if条件为真时if里的语句才会执行。x > y返回true,因此”y is greater than x”会在终端上打印。




下面的例子展示逻辑与操作符。


#!/usr/bin/ruby

puts true && true
puts true && false
puts false && true
puts false && false

与操作只有在操作数都为true才返回true。


$ ./andoperator.rb

true

false

false

false


只有一个表达式的结果为true。




逻辑或操作符当有一个操作数为true则返回true。


#!/usr/bin/ruby

puts true || true
puts true || false
puts false || true
puts false || false

如果有一边是true,操作的结果就是true。


$ ./oroperator.rb

true

true

true

false


三个表达式结果为true。




非操作进行真假反转。


#!/usr/bin/ruby

puts !0
puts !1
puts !true
puts !false

puts ! (4<3)
puts ! “Ruby”.include?(“a”)

这个例子展示了非操作符的用法。


$ ./not.rb

false

false

false

true

true

true


输出结果。




||&&操作符是短路求值(short circuit evaluated)。短路求值意味着只有在第一个参数不足以确定表达式的值时第二个参数才会进行求值。短求值主要用于提高执行效率。


用一个例子解释。


#!/usr/bin/ruby

def one
puts “Inside one”
false
end

def two
puts “Inside two”
true
end

puts “Short circuit”

if one && two
puts “Pass”
end

puts “##############################”

if two || one
puts “Pass”
end

在例子中定义了两个方法,用于在布尔操作中作为操作数。我们将看到它们是否被调用了。


if one && two
puts “Pass”
end

第一个方法返回false,短路求值不会计算第二个方法。一旦一个操作数是false,那么这个逻辑的结果总是false。


puts “##############################”

if two || one
puts “Pass”
end

第二种情况我们使用||操作符,并且第一个操作数为tow方法。这里同样没有必要对第二个操作数求值,只要第一个操作数为true,那么逻辑或总是为true。


$ ./shortcircuit.rb
Short circuit
Inside one
##############################
Inside two
Pass

shortcircuit.rb程序的运行结果。


关系操作符


关系操作符用于值的比较,其总是返回布尔值。



























符号含义
<小于
<=小于等于
>大于
>=大于等于

关系操作符又称为比较操作符。


#!/usr/bin/ruby

p 3 < 4
p 3 > 5
p 3 >= 3

表达式3 < 4返回true,因为3小于4。表达式3 > 5返回false,因为3不大于5。


位操作符


人类用的是十进制数字,计算机原生的是二进制数。二进制、八进制、十进制和十六进制只是数字的符号。位操作符针对的是二进制数。



































符号 含义
~ 按位取反
^ 按位异或
& 按位与
| 按位与或
<< 左移位
>> 右移位


位操作在高级语言中很少使用。


#!/usr/bin/ruby

puts ~ 7 # prints -8
puts ~ -8 # prints 7

puts 6 & 3 # prints 2
puts 3 & 6 # prints 2

puts 6 ^ 3 # prints 5
puts 3 ^ 6 # prints 5

puts 6 | 3 # prints 7
puts 3 | 6 # prints 7

puts 6 << 1 # prints 12
puts 1 << 6 # prints 64

puts 6 >> 1 # prints 3
puts 1 >> 6 # prints 0

上面的例子中展示了这6个位操作符。


puts ~ 7   # prints -8
puts ~ -8 # prints 7

按位取反是将1变为0,0变为1。将操作会将数字7所有的位都反转,同样包括符号位。如果再次反转所有的位即会得到数字7。


puts 6 & 3  # prints 2
puts 3 & 6 # prints 2

按位与是将两个数进行逐位与操作,只有两个数对应位的都为1结果才为1。


puts 6 ^ 3  # prints 5
puts 3 ^ 6 # prints 5

按位异或是将两个数进行逐位异或操作,只要其中一个数(但不是全部)对应位的为1结果就为1。


puts 6 | 3  # prints 7
puts 3 | 6 # prints 7

按位或是将两个数进行逐位或操作,只要其中一个数对应位的为1结果就为1。


puts 6 << 1  # prints 12
puts 1 << 6 # prints 64

puts 6 >> 1 # prints 3
puts 1 >> 6 # prints 0

移位操作符是按位进行右移或左移,也称为算术移位。


复合分配操作符


复合分配操作符是由两个操作符构成。它们是简写操作符。


#!/usr/bin/ruby

a = 0

a = a + 1
a += 1
puts a


b = 0

b = b - 8
b -= 8
puts b

+=-=操作符是一个简写的操作符。它们的可读性没有全写的好,但是有经验的程序员经常使用它们。


a = a + 1
a += 1

这两行的操作是一样的,都是将变量a加1。


其他的一些复合操作符。


-=   *=  =  /=   %=   &=   |=   <<=   >>=

操作符优先级


操作符的优先级表明了先对哪个操作符进行求值。优先级避免了表达式的二义性。


这个表达式的结果是多少?28还是40?


3 + 5 5


跟数学相同,乘法优先级高于加法,加些结果为28。


(3 + 5) 5


我们可以使用括号来改变优先级。括号里的表达式总是最先求值。


#!/usr/bin/ruby

puts 3 + 5 5
puts (3 + 5)
5

puts ! true | true
puts ! (true | true)

这个例子展示了一些表达式,其结果依赖于操作符优先级。


puts 3 + 5  5

这行打印28,因为乘法操作优先级高于加法。


puts ! true | true

这里非操作优先级更高。最后返回为true。


$ ./precedence.rb

28

40

true

false


结合律


有时优先级不能决定表达式的结果。还有另一条规则称为结合律。它决定相同优先级的求值顺序。


9 / 3 3


这个的结果是多少?9还是1?乘、除和取模操作是从左到右结合的。因此结果为9。


数学运算、布尔、关系和位操作都是从左向右结合的。


另外赋值操作是右结合。


a = b = c = d = 0
print a, b, c, d # prints 0000

复合分配操作是从右向左结合的。


j = 0
j *= 3 + 1
puts j

你可能期望结果为1,但是实际上结果为0。由于结合律,右边表达式先求值再应用复合分配操作。


范围操作符


Ruby有两个范围操作符,用于创建一个范围对象。通常是数字或者字母的范围。


..范围操作符(两个点)创建一个包含的范围。操作符(三个点)创建一个非包含的范围,较大的值不包含在其中。


#!/usr/bin/ruby

p (1..3).to_a
p (1…3).to_a

p (‘a’ .. ‘l’).to_a

这个例子中我们使用范围操作符创建了数字范围和字母范围。


p (1..3).to_a
p (1…3).to_a

这两行都是使用范围操作符创建了两个范围,然后转换为数据。第一个范围的值为1,2,3;第二个为1和2.


p (‘a’ .. ‘l’).to_a

这里使用..操作符创建一个’a’到’l’的数组。


$ ./range.rb

[1, 2, 3]

[1, 2]

[“a”, “b”, “c”, “d”, “e”, “f”, “g”, “h”, “i”, “j”, “k”, “l”]


输出结果。


三元运算术


三元运算术是一个条件操作。如果我们需要根据条件表达式从两个值选择一个时这是一个方便的操作。


cond-exp ? exp1 : exp2


如果cond-exp为真则返回exp1,否则返回exp2


#!/usr/bin/ruby

age = 32

adult = age >= 18 ? true : false

if adult then
puts “Adult”
else
puts “Not adult”
end

许多国家成年是基于你的年龄。是否成年即为是否超过了一定年龄。这种情况适合于三元操作。


adult = age >= 18 ? true : false

首先对赋值符右转进行求值,它的值将赋给adult变量。


$ ./ternary.rb

Adult


32年已经成年了。


计算素数


我们将计算素数。


#!/usr/bin/ruby

nums = (4..50).to_a

puts “Prime numbers:”

print “2 3 “

nums.each do |i|

not_prime = false

(2..Math.sqrt(i).ceil).each do |j|
not_prime = true if i % j == 0
end

print i, “ “ unless not_prime

end

puts

上面的例子中我们用到了一些操作符。


nums = (4..50).to_a

我们将从这些数中计算哪些是素数。


print “2 3 “

我们路过2和3,因为它们都是素数。


not_prime = false

not_prime标志表明选择的数不是素数。我们假设选择的数是素数,直到被证明不是为止。


(2..Math.sqrt(i).ceil).each do |j|
not_prime = true if i % j == 0
end

当取模的结果为0表示这个数不是素数。


print i, “ “ unless not_prime

如果not_prime标志没有设置则打印这个数。




上面例子展示了一些操作符。实际上有更简单方法计算素数。Ruby中有一个计算素数的模块。


#!/usr/bin/ruby

require ‘prime’

Prime.each(50) do |i|
print i, “ “
end

puts

使用Ruby的prime模块计算素数。


require ‘prime’

导入prime模块。


Prime.each(50) do |i|
print i, “ “
end

计算到50的素数。


$ ./primes.rb

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47


结果输出了2到50之间的素数。


这章的教程介绍了表达式。




原文地址: http://zetcode.com/lang/rubytutorial/expressions/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程7——字符串

字符串

在这部分的教程将详细的介绍字符串。


字符串是计算机语言中最重要的数据类型之一,这就是为什么我们决定要用一整章来介绍字符串。


字符串是序列化的unicode字符。它是一种数据类型存储了序列化的值,它的元素通常代表的是根据字符编码的字符。当一个字符串在代码中以字面量出现,它也称为字符串文本。


第一个例子


在Ruby中字符串文本是以单引号或者双引号引起来的。


#!/usr/bin/ruby

# first.rb

puts ‘Python language’
puts “Ruby language”

这个例子有两个字符串,第一个是用单引号,另一个是用双引号。


$ ./first.rb

Python language

Ruby language


程序输出。


使用引号


如果我们想要显示引号该怎么做,直接引用?这有两种方法实现。


#!/usr/bin/ruby

puts “There are many stars”
puts “He said, \”Which one is your favourite?\””

puts ‘There are many stars’
puts ‘He said, “Which one is your favourite?”‘

我们使用()字符将引号转义。通常双引号是用于划定字符串的范围,然后被转义之后它原始的意义就没有了。它在字符串中被作为一个正常的字符。第二种方法是混合使用单引号和双引号。


$ ./quotes.exe

There are many stars.

He said, “Which one is your favourite?”


输出结果。


转义序列


转义符是特殊的字符,在字符串中有特殊的含意。


#!/usr/bin/ruby

puts “one two three four”
puts “one\ntwo\nthree\nfour”

最常用一个的转义符是换行符\n。它在许多编程语言中都有效。换行符之后的字符将出现在新的一行。


$ ./newline.rb

one two three four

one

two

three

four


以上脚本的输出结果中换行符之后的字符在新的一行。




rbt是正常的字符,当前面加上\字符后就具有特殊意义了。


#!/usr/bin/ruby

puts “ bbb\raaa”
puts “Joan\b\b\bane”
puts “Towering\tinferno”

这个例子中我们用到了三个不同的转义字符。


puts “   bbb\raaa”

回车符\r控制之后的字符回到行首。之前打印到终端上的字符串会被处理。这个转义字符会将aaa字符位于bbb之前,输出为aaabbb


puts “Joan\b\b\bane”

\b是退格控制符,它会删掉前一个字符。这个字符串将在终端上打印‘Jane’而不是‘Joan’


puts “Towering\tinferno”

最后\t转义符是用于两个词之间制表空格的。


$ ./escapes.rb

aaabbb

Jane

Towering inferno


例子的输出结果。




反斜杠\是用于创建转义符的特殊字符。当需要打印反斜杠时,则在它前面加上另一个反斜杠。这意味着转义过了可以打印。在Ruby中单引号和双引号用于划定字符串范围的,想要打印它们同样也需要在前面加上\


#!/usr/bin/ruby

puts “The special character \“
puts “The special character \’”
puts “The special character \””

这个例子中我们在终端上打印了这三个字符。


$ ./specials.rb

The special character \

The special character ‘

The special character “


输出结果。


访问字符串的元素


在Ruby中我们可以使用方括号[]来访问字符串的元素。在括号内可以使用字符串、范围索引。


#!/usr/bin/ruby

msg = “Ruby language”

puts msg[“Ruby”]
puts msg[“Python”]

puts msg[0]
puts msg[-1]

puts msg[0, 3]
puts msg[0..9]
puts msg[0, msg.length]

这个例子的代码展示了我们可以访问字符串的一部分。


msg = “Ruby language”

这里是我们将要访问的字符串。


puts msg[“Ruby”]

这行代码我们测试字符串‘Ruby’是否是msg的子串。如果是将返回该字符串。


puts msg[0]

可以通过索引来访问这个字符串的字符。编号是从0开始的,也就是说0号索引是第1个字符。msg[0]返回字符串的第一个字符R


puts msg[-1]

这里我们访问最后一个字符。-1代表了最后一个索引。


puts msg[0, 3]

两个用逗号分隔的索引返回了从第1个索引开始到第2个索引的字符,不包括第2个索引。


puts msg[0..9]

范围操作符也可以完成同样操作。这里我们打印前10个字符。


puts msg[0, msg.length]

这行返回全部的字符。msg.length返回字符串的长度。


$ ./access.rb

Ruby


R

e

Rub

Ruby langu

Ruby language


输出结果。


多行字符串


许多编程语言要创建多行字符串需要额外的努力,在Visual Basic中尤其如此。但是在Ruby中却是很容易。


#!/usr/bin/ruby

puts “I hear Mariachi static on my radio
And the tubes they glow in the dark
And I’m there with her in Ensenada
And I’m here in Echo Park


puts %/
Carmelita hold me tighter
I think I’m sinking down
And I’m all strung out on heroin
On the outskirts of town/

puts <<STRING

Well, I’m sittin’ here playing solitaire
With my pearl-handled deck
The county won’t give me no more methadone
And they cut off your welfare check
STRING

这个例子中我们使用了Carmelita歌曲的歌词。我们展示了使用三种方法打印多行字符串。可以使用双引号;可以使用%字符创建多行字符串,用%后面的字符将字符串包围住;最后我们使用定界符,这和语法是使用<<接着一些字符串,用这个字符串包围住多行字符。它必须左对齐。


变量替换


变量替换是将字符串的变量替换成它们的值。为了将变量替换成值,这个变量在字符串需要放在#{}之间。


#!/usr/bin/ruby

name = “Jane”
age = 17

puts “#{name} is #{age} years old”

这个例子中我们替换了字符串中的两个变量:nameage


$ ./interpolation.rb

Jane is 17 years old




也可以替换表达式。


#!/usr/bin/ruby

x = 5
y = 6

puts “The product of #{x} and #{y} is #{xy}”

这是一个表达式替换的例子。


$ ./interpolation2.rb

The product of 5 and 6 is 30


运行例子脚本。




这是另一种替换变量的方法。类似于Python 2.x所支持的替换。


#!/usr/bin/ruby

name = “Jane”
age = 17

message = “%s is %d years old” % [name, age]
puts message

以上是一个例子。


message = “%s is %d years old” % [name, age]

使用之前先创建一个字符串。%s%d是格式化字符,分别表示字符串和数字。值由%字符后面的方括号提供。


字符串连接


字符串连接是从多个字符串创建一个字符串。


#!/usr/bin/ruby

lang = “Ruby” + “ programming” + “ languge”
puts lang

lang = “Python” “ programming” “ language”
puts lang

lang = “Perl” << “ programming” << “ language”
puts lang

lang = “Java”.concat(“ programming”).concat(“ language”)
puts lang

Ruby提供多种方法连接字符串。


lang = “Ruby” + “ programming” + “ languge”

在计算机语言中加法操作符是最常用的一种。Perl和PHP是用的点操作符(.)。


lang = “Python” “ programming” “ language”

Ruby自动连接多个后续的字符串。


lang = “Perl” << “ programming” << “ language”

另一种方法是使用<<操作符。


lang = “Java”.concat(“ programming”).concat(“ language”)

每个字符串字面符实际上都是一个对象。我们可以对Ruby对象调用很多方法。对于字符串对象可以调用concat方法连接两个字符串。它返回最后的结果对象,再对其调用另一个方法。这样我们就将这些方法链接起来了。


$ ./concatenate.rb

Ruby programming languge

Python programming language

Perl programming language

Java programming language


输出结果。


冻结字符串


在Java或者C#中字符串是不可变的,这意味着不能修改已存在的字符串。只能通过已存在的字符串创建一个新的。在Ruby中字符串同样也是不可修改的。


Ruby中字符串对象有一个freeze方法,可以使它们不可变。


#!/usr/bin/ruby

msg = “Jane”
msg << “ is “
msg << “17 years old”

puts msg

msg.freeze

#msg << “and she is pretty”

这个例子我们演示了字符串是可变的。然后在调用freeze方法之后我们就不能再作修改了。如果我们取消掉注释将会得到’can’t modify frozen string’错误信息。


字符串比较


在编程中字符串比较是件平常的事情。我们可以使用==操作符或者eql?方法来比较两个字符串,如果相等返回true,否则false


#!/usr/bin/ruby

puts “12” == “12”
puts “17” == “9”
puts “aa” == “ab”

puts “Jane”.eql? “Jan”
puts “Jane”.eql? “Jane”

这个例子的代码我们比较了一些字符串。


puts “12” == “12”

这两个是相等的,这行返回true。


puts “aa” == “ab”

第一个字符是相等,然后比较下一个字符。它们不同,因此返回false。


puts “Jane”.eql? “Jan”

eql?方法用于比较两个字符串。所有对象都内建了eql?方法。这个方法将另一个要比较的字符串作为参数传入。


$ ./comparing.rb

true

false

false

false

true


程序输出结果。




Ruby中有一个“飞船(spaceship)”操作符<==>。这个操作符来自Perl,与以上两个比较方法不同,它不是返回truefalse,而是返回1、0或者-1。取决于左参数和右参数的关系。如果左参数大于右参数则返回1;如果左参数小于右参数则返回-1;如果相等返回0。什么才是一个字符大于另一个字符呢?字符在表中是有序的,在表中每个字符有一位位置。当比较字符时实际比较它们在表中的位置。例如ASCII表,字符ab的前面,因此a<==>b返回-1,因为左参数的位置小于右参数b


#!/usr/bin/ruby

puts “a” <==> “b”
puts “b” <==> “a”
puts “a” <==> “a”

使用“飞船”操作符比较字符串。


$ ./spaceship.rb

-1

1

0


输出结果。




有可能比较字符串不管大小写。在Ruby中有casecmp方法。这个方法与飞船操作符相同。


#!/usr/bin/ruby

puts “Jane”.casecmp “Jane”
puts “Jane”.casecmp “jane”
puts “Jane”.casecmp “Jan”

以上是一个例子。


puts “Jane”.casecmp “Jane”
puts “Jane”.casecmp “jane”

这两行返回结果都是0.因为我们不考虑字符的大小写。


$ ./case.rb

0

0

1


输出结果。


字符串对象


Ruby是面向对象语言。对象是面向对象程序的基本代码块。字符串也是对象。对象由数据和方法组成。在面向对象程序中,创建对象并且相互通信。


#!/usr/bin/ruby

website = “google.com”
puts website

website = String.new “zetcode.com”
puts website

以上例子我们展示了两个创建字符串的基本方法。


website = “google.com”

这里是使用字符串字面量创建了一个website字符串变量。


website = String.new “zetcode.com”

这是标准创建字符串对象的方法。然而第一和更常用,因为它更简洁,并且在计算机语言中更普遍。




#!/usr/bin/ruby

puts “zetcode”.upcase
puts “zetcode”.size
puts “zetcode”.reverse

这个例子中我们对字符串字面量调用了三个方法。如果是熟悉Java或者C的人可能会困惑。在Ruby中调用方法时字符串字面量会转化为一个字符串对象。


$ ./stringobject2.rb

ZETCODE

7

edoctez


输出结果。


字符串方法


Ruby字符串对象有一些有用的方法。如我们见到过的concat或者eql?


#!/usr/bin/ruby

word = “Determination”

puts “The word #{word} has #{word.size} characters”

puts word.include? “tion”
puts word.include? “tic”

puts

puts word.empty?
word.clear
puts word.empty?

创建一个字符串变量,展示了四个字符串的方法。


puts “The word #{word} has #{word.size} characters”

size方法返回字符串的长度。


puts word.include? “tion”

include?方法判断一个字符串是否为子串。这里返回的是true。


puts word.empty?
word.clear

empty?方法检查字符串是否为空。返回一个布尔值。clear方法清空字符串。


$ ./basicmethods.rb

The word Determination has 13 characters

true

false


false

true


输出结果。




接下来的例子介绍字符串大小写的方法。


#!/usr/bin/ruby

ruby = “Ruby programming language”

puts ruby.upcase
puts ruby.downcase
puts ruby.capitalize
puts ruby.swapcase

Ruby中有四个与大小写相关的方法。upcase方法将字符串全转为大写并返回一个新对象;downcase方法是将字符串全转为小写并返回一个新对象;capitalize方法是将字符串的首字母大写并返回一个新的对象。最后swapcase方法是将大小写反转并返回一个新对象。


$ ./rubylang.rb

RUBY PROGRAMMING LANGUAGE

ruby programming language

Ruby programming language

rUBY PROGRAMMING LANGUAGE


输出结果。




接下来展示两个Ruby字符串的方法:start_with?end_with?。这两个方法都是返回布尔值。它们判断一个字符串是否分别以另一个字符串开头或结尾。


#!/usr/bin/ruby

ws1 = “zetcode.com”
ws2 = “www.gnome.org”

puts ws1.start_with? “www.”
puts ws2.start_with? “www.”

puts

puts ws1.end_with? “.com”
puts ws2.end_with? “.com”

这是一个上述方法的例子。


puts ws1.start_with? “www.”

这里检查一个字符串是否以“www.”开头。如果不是则在终端打印false。


puts ws1.end_with? “.com”

检查字符串ws1是否以”.com”结尾。如果是则在终端打印true。


$ ./startend.rb

false

true


true

false


输出结果。




接下来我们介绍inspect方法。这个方法返回被引号包围原始的字符串,对于特殊字符不处理。对于想要检查字符串由哪些字符组成的比较有用。


#!/usr/bin/ruby

msg = “Jane\t17\nThomas\t23”

puts msg
puts msg.inspect

inspect方法的一个例子。


msg = “Jane\t17\nThomas\t23”

这个字符串有一些特殊的字符。


puts msg
puts msg.inspect

第一种情况,字符串中的制表符和换行符被解释了。第二种情况我们得到了一个原始格式的字符串。


$ ./inspectmethod.rb
Jane 17
Thomas 23
“Jane\t17\nThomas\t23”

输出结果。




chomp方法是将字符串尾部的分隔符去掉并返回一个新对象。默认的分隔符是换行符(\n)。


#!/usr/bin/ruby

print “Are you sure to download? (Yes/No) “

response = gets

if (response.downcase == “yes”)
puts “Downloaded”
else
puts “Download cancelled”
end

puts response.inspect

上面的脚本我们从用户输入一些内容,然后再返回给用户。


$ ./chomp.rb

Are you sure to download? (Yes/No) Yes

Download cancelled

“Yes\n”


这个脚本没有正确的工作。原因很清晰,由inspect返回了。用户输入的内容以回车键结尾,因此换行符也被包含其中了。”Yes”与”Yes\n”不相等。要使用脚本正确运行,我们使用chomp方法移除掉换行符。


#!/usr/bin/ruby

print “Are you sure to download? (Yes/No) “

response = gets

if (response.downcase.chomp == “yes”)
puts “Downloaded”
else
puts “Download cancelled”
end

puts response.inspect

这个是正确的脚本。


if (response.downcase.chomp == “yes”)

这里我们在比”Yes”比较之前对输入内容进行了处理。


$ ./chomp.rb

Are you sure to download? (Yes/No) Yes

Downloaded

“Yes\n”


现在脚本正确工作了。


格式化字符串


Ruby中有格式化说明符。格式化说明符决定了如何显示一个字符串。它以%字符开始。格式化说明符放在单引号或者双引号内。


格式化说明符有以下字段。


%[flags][field width][precision]conversion specifier


方括号内的字段是可选的。


转换说明符表明数据将如何进行转换。


#!/usr/bin/ruby

puts “There are %d oranges in the basket.” % 12
puts “There are %d oranges and %d apples in the basket.” % [12, 10]

这是一个格式化说明符的例子。


puts “There are %d oranges in the basket” % 12

当我们在字符串中使用%d,我们在这个位置将期望一个数字。d是十进制数的转换说明符。这个数据在%字符后面给出。


puts “There are %d oranges and %d apples in the basket” % [12, 10]

当我们在一个字符串中使用多个格式化说明符时,每个都以%开始。多个值放在[]之间并以逗号分隔。


$ ./formatspecifiers.rb

There are 12 oranges in the basket.

There are 12 oranges and 10 apples in the basket.


输出结果。




接下来的例子我们介绍一些基本的转换符。


#!/usr/bin/ruby

puts “There are %d apples.” % 5
puts “I can see %i oranges.” % 3
puts “The width of iPhone 3G is %f mm.” % 62.1
puts “This animal is called a %s” % “rhinoceros.”

我们使用了整数、浮点数和字符串的转换符。


puts “There are %d apples.” % 5
puts “I can see %i oranges.” % 3

di都是用于整数。


puts “The width of iPhone 3G is %f mm.” % 62.1

f用于浮点数。默认浮点数有6个小数。


puts “This animal is called a %s” % “rhinoceros.”

s用于字符串。


$ ./basicspecifiers.rb

There are 5 apples.

I can see 3 oranges.

The width of iPhone 3G is 62.100000 mm.

This animal is called a rhinoceros.


输出结果。




接下来是一个实践的例子。


#!/usr/bin/ruby

website = “zetcode.com”

website.each_char do |c|
print “#{c} has ASCII code %d\n” % c.ord
end

这个例子我们记遍历字符串的所有字符并在终端上打印它的ASCII值。


website.each_char do |c|
print “#{c} has ASCII code %d\n” % c.ord
end

each_char方法将website字符串中的每个字符传给代码块,一个字符是一次循环。当前的字符保存在这是c中。我们使用ord方法获取字符的ASCII值。


$ ./character.rb

z has ASCII code 122

e has ASCII code 101

t has ASCII code 116

c has ASCII code 99

o has ASCII code 111

d has ASCII code 100

e has ASCII code 101

. has ASCII code 46

c has ASCII code 99

o has ASCII code 111

m has ASCII code 109


输出结果。




数字有多种显示形式。


#!/usr/bin/ruby

# decimal
puts “%d” % 300

# hexadecimal
puts “%x” % 300

# octal
puts “%o” % 300

# binary
puts “%b” % 300

# scientific
puts “%e” % (5/3.0)

上面的例子中我们打印数字的十进制、十六进制、八进制、二进制和科学记数的形式。


# hexadecimal
puts “%x” % 300

x转换符用于将数字转为十六进制格式。


# binary
puts “%b” % 300

b转换符用于将数字转为二进制格式。


$ ./various.rb

300

12c

454

100101100

1.666667e+00


输出结果。




精度是格式化说明符的一个字段。它指明了后面小数点的位数。它对于整数、小数和字符串有不同的意义。当用于整数时表明了最少打印多少个数字。如果数字个数少于精度则前面用0补全。对于整数精度默认为1,表示前面没有0.当用于浮点数时表示小数点后显示多少个数字。最后,用于字符串时精度表示多少个字符会打印。


#!/usr/bin/ruby

puts ‘Height: %f %s’ % [172.3, ‘cm’]
puts ‘Height: %.1f %s’ % [172.3, ‘cm’]

puts “%d” % 16
puts “%.5d” % 16

puts “%s” % “zetcode”
puts “%.5s” % “zetcode”

这个例子中我们使用了精度字段。


puts ‘Height: %f %s’ % [172.3, ‘cm’]
puts ‘Height: %.1f %s’ % [172.3, ‘cm’]

172.3是一个浮点数。如果没有指定精度则会显示6个小数。这里将会显示5个0.第二行的.1是精度,它将小数个数减为1。的


puts “%d” % 16
puts “%.5d” % 16

对于整数默认精度为1。第二行中我们指明了精度为.5,这将会在16前面加上3个0。


puts “%s” % “zetcode”
puts “%.5s” % “zetcode”

第一行会打印所有的字符串。第二行仅打印5个,有两个字符将抛弃。


$ ./precision.rb

Height: 172.300000 cm

Height: 172.3 cm

16

00016

zetcode

zetco


输出结果。




宽度字段表示数据最小要显示的宽度。它是一个数字,并且在小数点之前。如果输出比较短,则会被用空格填充,且右对齐。如果宽度前面有减号则会左对齐。如果输出比宽度长则会完整输出。


#!/usr/bin/ruby

puts “%d” % 1
puts “%d” % 16
puts “%d” % 165
puts “%d” % 1656
puts “%d” % 16567

puts “%10d” % 1
puts “%10d” % 16
puts “%10d” % 165
puts “%10d” % 1656
puts “%10d” % 16567

第一种情况打印5个数字没有指明宽度。输出的宽度即为数字的位数。第二种情况指明宽度为10。则每个都最少输出10字符的长度,数字右对齐。


puts “%d” % 1
puts “%d” % 16

打印两个数字,输出的宽度分别为1、2个字符。


puts “%10d” % 1
puts “%10d” % 16

这里宽度都为10个字符。这两个数被填充了8、9个空格。


$ ./fieldwidth.rb
1
16
165
1656
16567
1
16
165
1656
16567

我们发现第二种情况数字是右对齐的。




标志修饰符修改了模式的行为。


#标志分别在二进制、八进制各十六进制前加上0b、0和0x。它会给小数加上小数点,即使这个小数人被精度限制了。


#!/usr/bin/ruby

puts “%#b” % 231
puts “%#x” % 231
puts “%#o” % 231

puts “%.0e” % 231
puts “%#.0e” % 231

puts “%.0f” % 231
puts “%#.0f” % 231

这个例子我们使用了#标志。


puts “%#b” % 231
puts “%#x” % 231
puts “%#o” % 231

十进制数231分别以二进制、八进制和十六进制打印。#标志会给它们加上前缀。


puts “%.0e” % 231
puts “%#.0e” % 231

这个.0限制了小数个数。然后使用#标志后小数点会显示,即使没有小数值。


$ ./flags1.rb

0xe7

0b11100111

0347

2e+02

2.e+02

231

231.


输出结果。




+标志会给正数加上正号。对于二进制、十六进制和八进制的负数会加上负号并使用它的绝对值。


#!/usr/bin/ruby

puts “%d” % 231
puts “%+d” % 231
puts “%d” % -231
puts “%+d” % -231

puts “%b” % -231
puts “%o” % -231
puts “%x” % -231

puts “%+b” % -231
puts “%+o” % -231
puts “%+x” % -231

一个演示+标志符的例子。


puts “%d” % 231
puts “%+d” % 231

通常正数的符号会被忽略掉。如果想要显示正数的符号可以使用+标志符。


puts “%d” % -231
puts “%+d” % -231

+标志符对负数没有影响,输出结果还是一样的。


puts “%b” % -231
puts “%o” % -231
puts “%x” % -231

二进制、八进制和十六进制的数有它们自己生成负数的方法。


puts “%+b” % -231
puts “%+o” % -231
puts “%+x” % -231

如果我们对负数指定了+标志,我们将数字转为不同的格式并加上负号。没有专门的方式表示负数。


$ ./flags2.rb

231

+231

-231

-231

..100011001

..7431

..f19

-11100111

-347

-e7


输出结果。




这里介绍0标志和-标志。0标志会用0代替空格来填充数字。-标志会将输出左对齐。


#!/usr/bin/ruby

puts “%010d” % 1
puts “%010d” % 16
puts “%010d” % 165
puts “%010d” % 1656
puts “%010d” % 16567

puts “%-10d” % 1
puts “%-10d” % 16
puts “%-10d” % 165
puts “%-10d” % 1656
puts “%-10d” % 16567

以上是一个例子。


puts “%010d” % 1
puts “%010d” % 16

数字会用0填充。


puts “%-10d” % 1
puts “%-10d” % 16

数字个数比宽度短,且会左对齐。


$ ./fieldwidth2.rb

0000000001

0000000016

0000000165

0000001656

0000016567

1

16

165

1656

16567


输出结果。




标志符用于精度和宽度。当我们使用符号时精度和宽度将作为一个参数。


#!/usr/bin/ruby

puts “%.
f” % [3, 1.1111111]
puts “%0d” % [10, 2]
puts “%0
.f” % [10, 3, 1.1111]

使用了标志的的例子。


puts “%.f” % [3, 1.1111111]

这里使用作为精度,则第一个数字3是精度的参数。它只为显示1.1111111的3个小数。


puts “%0d” % [10, 2]

这行的代码我们使用标志作为宽度。我们必须在[]之间加上宽度。第一个数字是宽度,第十个数字是要转换的值。


puts “%0.f” % [10, 3, 1.1111]

*标志可以同时用于宽度和精度。这样我们就必须都在[]中指明。


$ ./flags3.rb

1.111

0000000002

000001.111


输出结果。


这章的教程介绍的Ruby的字符串。




原文地址: http://zetcode.com/lang/rubytutorial/strings/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程6——数据类型

数据类型

在这一章的教程中我们开始讨论数据类型。


所有类别的计算机程序,包括电子表格、文本编辑器、计算器和聊天软件都使用数据。现代计算机语言中各种数据类型是必不可少的。一种数据类型是一些可操作的值的集合。


Ruby中有一些数据类型,这些类型都是基于类的。以下是Ruby中公认的数据类型:



  • 布尔(Booleans)

  • 符号(Symbols)

  • 数字(Numbers)

  • 字符串(Strings)

  • 数组(Arrays)

  • 哈希(Hashes)


下面的例子中我们展示Ruby中所有重要的数据类型。


#!/usr/bin/ruby

h = { :name => “Jane”, :age => 17 }

p true.class, false.class
p “Ruby”.class
p 1.class
p 4.5.class
p 3_463_456_457.class
p :age.class
p [1, 2, 3].class
p h.class

我们将它们的类型名打印出来,类型是用于创建对象的模块。


p true.class, false.class

truefalse对象展示的布尔类型。


p “Ruby”.class

这个是字符串。


p 1.class
p 4.5.class
p 3_463_456_457.class

这些是数字。


p :age.class

这个是符号标志,Ruby的一种特殊数据类型。


p [1, 2, 3].class
p h.class

这是两个容器,数组和哈希表。


$ ./types.rb

TrueClass

FalseClass

String

Fixnum

Float

Bignum

Symbol

Array

Hash


这个程序列出了属于Ruby的类型。


布尔值


在我们的世界里存在着对偶关系。天与地、水与火、男与女、爱与恨。这些就是我们自然中的“布尔”。在Ruby里,布尔类型只有一种,具有两个值:true(真)与false(假)。布尔是一种基本的数据类型,它在计算机程序中非常普遍。


高兴的父母等待着孩子的诞生。他们可能会给孩子取个名字。如果是男孩就叫John,如果是女孩就叫Victoria。


#!/usr/bin/ruby

# kid.rb

bool = [true, false]

male = bool[rand(2)]


if male
puts “We will use name John”
else
puts “We will use name Victoria”
end

在这个程序中我们使用随机数来模拟这种情况。


bool = [true, false]

我们创建一个名为bool的变量,它是一个数组包含了两个布尔值。这个数组是使用方括号创建的。


male = bool[rand(2)]

我们使用rand()方法生成一个随机数。这个方法返回0或1。返回的数用于数组的索引。


if male
puts “We will use name John”
else
puts “We will use name Victoria”
end

根据male变量我们打印一条信息。如果maletrue则名字选择John,否则选择Vactoria。类似is/else的结构控制语句是根据布尔值来选择的。


$ ./kid.rb

We will use name Victoria

$ ./kid.rb

We will use name Victoria

$ ./kid.rb

We will use name John

$ ./kid.rb

We will use name John

$ ./kid.rb

We will use name John


程序运行多次的结果如上。


符号标志


符号标志代表其他对象。使用符号标志而不是字符串,是因为可能保存一些资源。一个符号标志是Symbol类的一个实例对象。符号标志通常是在标识符的前面加上冒号,比如 :name。一些对象具有to_sym方法,用于将这些对象转化为符号标志。


Ruby的符号标志在程序运行时是不可改变的。它通常用于当作哈希表的键,因为一个键不需要完整的字符串功能。


#!/usr/bin/ruby

p :name
p :name.class
p :name.methods.size
p “Jane”.methods.size

p :name.object_id
p :name.object_id
p “name”.object_id
p “name”.object_id

第一个例子展示了符号标志的基本操作。


p :name
p :name.class

在终端上打印一个符号标志和它的类型。符号标志的类型是Symbol


p :name.methods.size
p “Jane”.methods.size

比较字符串实例与符号标志实例分配的方法数量。字符串的方法数是符号标志的两位多。


p :name.object_id
p :name.object_id
p “name”.object_id
p “name”.object_id

相同的符号标志具有相同的id,相同的字符串的id却不同。


$ ./symbols.rb

:name

Symbol

79

162

10328

10328

77344750

77344730


程序输出结果。




符号标志也可以作为常量标志,类似于C/C++中的枚举类型。


#!/usr/bin/ruby

light = :on

if light == :on
puts “The light is on”
else
puts “The light is off”
end

light = :off

if light == :on
puts “The light is on”
else
puts “The light is off”
end

电灯不是开就是关。对于这两种情况定义了符号标志。


light = :on

灯是开的。


if light == :on
puts “The light is on”
else
puts “The light is off”
end

程序的逻辑依赖于light变量的状态。




符号标志通常在哗然容器中作为键。这比字符串更加有效。


#!/usr/bin/ruby

domains = {:sk => “Slovakia”, :no => “Norway”, :hu => “Hungary”}

puts domains[:sk]
puts domains[:no]
puts domains[:hu]

在这个脚本中创建了一个名为domains的哈希表。它的键全是符号标志。


puts domains[:sk]
puts domains[:no]
puts domains[:hu]

在哈希表中键是用于访问值的。接下来我们打印出这个哈希表的值。


$ ./symbols3.rb

Slovakia

Norway

Hungary


以上是例子的输出结果。




Ruby解释器将内部引用存储为符号标志。


#!/usr/bin/ruby

class Being

def initialize
@is = true
end

def say
“I am being”
end
end

b = Being.new

p b.method :say
p b.instance_variable_get :@is

定义了一个Being类。该类有一个自定义实例变量@is和一个方法say。这两个实体在Ruby中保存为符号标志。


p b.method :say

method方法用于在b对象在查找给定名字的方法。我们查找的是:say符号。


p b.instance_variable_get :@is

使用instance_variable_get检查@is是不是b对象的一个实例变量。在内部变量存储为:@is符号。


$ ./symbols4.rb



true


输出结果。




所有的符号都存储在符号表中。下一个例子我们来看看这个表。Symbol类的all_symbols方法返回了一个数组包含这个表的全部符号。


#!/usr/bin/ruby

def info
“info method”
end

@v = “Ruby”
@@n = “16”

p Symbol.all_symbols.include? :info
p Symbol.all_symbols.include? :@v
p Symbol.all_symbols.include? :@@n

在这个Ruby脚本中创建了一个方法、一个实例变量和一个类变量。我们检查这些实体是否存储在符号表中。


p Symbol.all_symbols.include? :info

检查:info符号是否在符号表中。这行返回的是true


$ ./symbols5.rb

true

true

true


三个符号都在符号表中。


整数


整数是实数的一个子集。它没有分数或者小数。整数属于集合Z = {…, -2, -1, 0, 1, 2, …} 。这个集合是无限的。


在计算机语言中,整数是原始的数据类型。实际中计算机仅支持整数的一个子集,因为计算机的能力有限。整数用于统计离散的实体。我们有3、4、6个人,但是不能有3.33个人。我们有3.33千克。


在Ruby中整数是Fixnum或者Bignum类的实例对象。不同于其他语言,如Java或者C,在Ruby中整数是一个对象。这两种类型的大小不同。Fixnum类型的整数有一些限制,这些限制与机器有关。Bignum的值表示范围比Fixnum大。如果一些操作超出了Fixnum的范围,它会自动的转换成Bignum。程序员通常不需要关心整数的类型。


#!/usr/bin/ruby

p -2
p 121
p 123265
p -34253464356
p 34867367893463476

p 1.class
p 23453246.class
p 234532423563456346.class
p 2345324235632363463456456346.class

p 5 / 2
p 5.div 2

这个例子中我们处理的一些整数。


p -2
p 121
p 123265
p -34253464356
p 34867367893463476

这是一些不同大小的正数和负数。


p 1.class
p 23453246.class
p 234532423563456346.class
p 2345324235632363463456456346.class

打印出这些数的类型。前两个整数是Fixnum类型,其余两个是Bignum类型。


p 5 / 2
p 5.div 2

这两行展示的整数的相除。当两个数相除时我们使用了相除操作符/方法,结果也是一个整数。


$ ./integers.rb

-2

121

123265

-34253464356

34867367893463476

Fixnum

Fixnum

Bignum

Bignum

2

2


例子的输出结果。




在Ruby中整数可以有不同的表现符号。十进制、十六进制、八进制和二进制数都是可用的。十六进制数以0x字符开头,八进制以0字符开头,二进制以0b字符开头。


#!/usr/bin/ruby

puts 122
puts 0x7a
puts 0172
puts 0b1111010

这个例子中我们打印了十进制数122的所有表现形式。


$ ./inotations.rb

122

122

122

122


以上是输出结果。




我们使用整数的话,那么就是用于处理离散的实体。也许我们用于统计苹果的数量。


#!/usr/bin/ruby

baskets = 16
apples_in_basket = 24

total = baskets apples_in_basket

puts “There are total of #{total} apples”

在这个程序中我们使用整数统计苹果的总数。


$ ./apples.rb

There are total of 384 apples


以上是程序的输出。




大数字读起来比较困难。如果我们有一个数245342395423452,我们发现很难快速的将它读出来。计算机之外大的数字是用空格或者逗号分隔开来。为了可读性,Ruby允许数字包含下划线。Ruby解释器会忽略整数中的下划线。


#!/usr/bin/ruby

p 23482345629
p 23_482_345_629

p 23482345629 == 23_482_345_629

这个例子演示了下划线的使用。


p 23482345629 == 23_482_345_629

这行展示了两个数据是相等的。


$ ./underscore.rb

23482345629

23482345629

true


例子的输出结果。


浮点数


在计算中浮点数表示实数。实例意味着连续的数量,如:重量、高度和速度。Ruby中小数是Float或者BigDecimal类对象。BigDecimal类是Ruby的核心类,是Ruby标准库的一部分。此外我们还可以使用Rational对象。


我们需要理解数字是不业精确的。Ruby的官方文档清楚地说道浮点数对象表示的是不精确的实数。


#!/usr/bin/ruby

p 15.4
p 0.3455
p -343.4563

p 12.5.class
p -12.5.class
p (5.0 / 2).class

p 5.fdiv 2
p 12.to_f

上面的这个例子我们使用了浮点数。


p 15.4
p 0.3455
p -343.4563

这里我们打印三个小数的值。小数包含了小数点字符。


p 12.5.class
p -12.5.class
p (5.0 / 2).class

以上展示的数字的类型,全都是浮点数。最后一个整数与浮点数相除结果为浮点数。


p 5.fdiv 2
p 12.to_f

这里我们创建浮点数除法fdiv和转换方法to_f来创建浮点数。


$ ./decimals.rb

15.4

0.3455

-343.4563

Float

Float

Float

2.5

12.0


以上为转出结果。




一个小数默认最多只显示小数点后16个数字。我们可以使用sprintf或者printf方法来控制浮点数的格式。


#!/usr/bin/ruby

p 1/3.0
p 1.fdiv 2

puts sprintf “%.4f” % (1/3.0)
puts sprintf “%.7f” % (5/3.0)

格式化小数。


p 1/3.0
p 13.fdiv 4
p 1.fdiv 2

第一行打印的小数在小数点后有16个数字。第二行打印的有两个。第三行打印的有一个。


puts sprintf “%.4f” % (1/3.0)
puts sprintf “%.7f” % (5/3.0)

这里我们使用sprintf方法控制小数点后面的数字个数。sprintf方法的格式说明符是精确的。它是一个数字接着%符号。f是一个转换说明符,表示处理的是一个浮点数。


$ ./formatfloat.rb

0.3333333333333333

3.25

0.5

0.3333

1.6666667


以上是输出结果。




Ruby支持使用科学的记数法来表示一个浮点数。这也称为指数记数法,它是将非常大或者非常小的数转换成正常的小数形式。


#!/usr/bin/ruby

p 1.2e-3
p 0.0012

p 1.5E-4
p 0.00015

这个例子展示了两个使用科学记数表示的小数。


$ ./scientific.rb

0.0012

0.0012

0.00015

0.00015


这是以上程序的输出。




前面我们主说过浮点数的值稍微有些不精确。在很多计算中,普通的浮点小数已经足够精确了,如:我们的体重是60kg还是60.000024kg并不重要。对于其他计算,包括科学和工程应用,精确度是非常重要的。


Ruby有一个BigDecimal标准库。这个类对于非常大或者非常精准的浮点数提供了任意的精度。


#!/usr/bin/ruby

require ‘bigdecimal’

sum = 0

1000.times do
sum = sum + 0.0001
end

p sum


sum = BigDecimal.new(“0”)

1000.times do
sum = sum + BigDecimal.new(“0.0001”)
end

puts sum.to_s(‘F’)
puts sum.to_s(‘E’)

在这个例子中我们比较了FloatBigDecimal的精度。


require ‘bigdecimal’

BigDecimal类必须导入。


sum = 0

1000.times do
sum = sum + 0.0001
end

p sum

通过循环对一个很小的浮点数求和。最后会出现一点误差。


sum = BigDecimal.new(“0”)

1000.times do
sum = sum + BigDecimal.new(“0.0001”)
end

然后再对BigDecimal做同样的事情。


puts sum.to_s(‘F’)
puts sum.to_s(‘E’)

sum浮点数打印为工程记数形式。


$ ./bigdecimal.rb

0.10000000000000184

0.1

0.1E0


输出结果表示BigDecimalFloat更精确。




如果一个短路运动员100m跑9.87s,那么他的时速是多少?


#!/usr/bin/ruby

distance = 0.1
time = 9.87 / 3600

speed = distance / time

puts “The average speed of a sprinter is #{speed} km/h”

这个例子必需使用浮点数。


distance = 0.1

100m等于0.1km。


time = 9.87 / 3600

9.87s等于9.87/6060 h。


speed = distance / time

要计算速度,我们用距离除以时间。


$ ./speed.rb

The average speed of a sprinter is 36.4741641337386 km/h


以上是speed.rb脚本的输出。


有理数


Ruby支持有理数。有理数是一个确切的数。使用有理数可以避免舍入错误。Ruby中有理数是Rational类的对象。我们可以使用某些对象的to_r方法来创建有理数。


有理数可以用于表示两个整数相除的分数,a/b(b!=0)。如果b为1,每个整数都是一个有理数。


#!/usr/bin/ruby

puts 2.to_r
puts “23”.to_r
puts 2.6.to_r

p Rational 0
p Rational 1/5.0
p Rational 0.5

这个例子展示了一些有理数。


puts 2.to_r

变量我们使用to_r方法将整数2转化为有理数2/1。


p Rational 0.5

使用Rational方法创建一个有理数》


$ ./rational.rb

2/1

23/1

5854679515581645/2251799813685248

(0/1)

(3602879701896397/18014398509481984)

(1/2)


例子的输出结果。


空值


Ruby有一个特殊的值nil。它表示空值。nilNilClass类的单实例,仅有一个nil


#!/usr/bin/ruby

puts nil
p nil

p $val

p [1, 2, 3][4]

p $val1 == $val2

nil的一个例子。


puts nil
p nil

在终端上打印nil的值。puts方法打印空字符串,p方法打印‘nil’字符串。


p $val

当我们引用一个没有定义的全局变量时会返回nil值。


p [1, 2, 3][3]

这行代码我们引用了一个3元素数组的第4个元素,结果返回nil。Ruby中的许多方法对于无效的值都返回nil


p $val1 == $val2

这行返回true,这实际上是因为nilNilClass的单实例对象。


$ ./nilvalue.rb


nil

nil

nil

true


输出结果。


字符串


字符串在计算机程序代表文本数据。Ruby字符串是一个序列化的unicode字符。字符串是String的一个对象。字符串的字面量是字符加上双引号或者单引号。


字符串是一个非常重要的数据类型。它需要专门用一章来介绍。这里我们仅包含一个小例子。


#!/usr/bin/ruby

p “Ruby”
p ‘Python’

p “Ruby”.size
p “Ruby”.upcase

p 23.to_s

这个例子中我们使用了Ruby的字符串。我们使用p方法是为了在输出中看到数据类型。


p “Ruby”
p ‘Python’

在终端中打印两个字符串的字面值。第一个字面量是使用双引号,第二个是单引号。


p “Ruby”.size
p “Ruby”.upcase

这两行调用了两个字符串的方法。size方法返回字符串的长度,在这里是4个字符。upcase方法是将字符串转为大写。


p 23.to_s

to_s方法是将整数转为字符串。


$ ./strings.rb

“Ruby”

“Python”

4

“RUBY”

“23”


在输出中我们看到字符串是在引号内。这就是我们使用p方法的结果,printputs方法都不会带上引号。


数组和哈希表


数组和哈希表是对象的集合。他们将对象集合在一起。


数组是对象的有序集合。哈希表是键-值对的集合。我们将会用单独的一章来介绍数组和哈希表。以下仅是一个预览的例子。


#!/usr/bin/ruby

nums = [1, 2, 3, 4]

puts “There are #{nums.size} items in the array”

nums.each do |num|
puts num
end


domains = { :de => “Germany”, :sk => “Slovakia”,
:us => “United States”, :no => “Norway” }

puts domains.keys
puts domains.values

这是一个Ruby数组和哈希表的例子。


nums = [1, 2, 3, 4]

puts “There are #{nums.size} items in the array”

nums.each do |num|
puts num
end

这里创建一个有4项内容的数组。第二行统计了这个数组数据项的数量,并合并到了消息中。随后我们使用each方法将每个元素打印在终端上。


domains = { :de => “Germany”, :sk => “Slovakia”,
:us => “United States”, :no => “Norway” }

puts domains.keys
puts domains.values

创建了Ruby的哈希表,然后打印它的键和值。


$ ./arrayshashes.rb

There are 4 items in the array

1

2

3

4

de

sk

us

no

Germany

Slovakia

United States

Norway


例子的输出结果。


类型转换


我们经常是一次使用多种数据类型。在编程中从一种数据类型转换为其他类型是很平常的。类型转换或者类型的引用会将一个实体从一个类型转换成另一个类型。有两种类型转换的方式:隐式和显式。隐式类型转换又称为强制转换,是被编译器自动的转换。Ruby只有显式转换。


Ruby有内建的转换方法。如:to_ito_s或者to_f。内核模块有一些公共的方法用来转换,如IntergerString或者Float。这些方法还要与Ruby的类混淆。


#!/usr/bin/ruby


p Array(1..6)
p Complex 6
p Float 12
p Integer “34”
p Rational 6
p String 22

这里我们展示了内核模块的转换方法。


$ ./convertmethods.rb

[1, 2, 3, 4, 5, 6]

(6+0i)

12.0

34

(6/1)

“22”


例子的输出结果。




#!/usr/bin/ruby

p “12”.to_i
p 12.5.to_i
p nil.to_i

p 12.to_f
p “11”.to_f
p nil.to_f

以上例子我们展示了数字的转换。一些对象具有to_ito_f方法将对象转换成整数和浮点数。


p “12”.to_i
p 12.5.to_i
p nil.to_i

这里我们将字符串、小数和nil转换成整数。


p 12.to_f
p “11”.to_f
p nil.to_f

这三行将整数、字符串和nil转换成小数。


$ ./conversions.rb

12

12

0

12.0

11.0

0.0


例子的输出结果。




第二个例子展示了一些字符串的转换。


#!/usr/bin/ruby

p “12”.to_i
p “13”.to_f
p “12”.to_r
p “13”.to_c

p “Jane”.to_sym

v = “Ruby Python Tcl PHP Perl”.split
p v.class

上面的例子中我们将字符串转换为不同类型的对象。


p “12”.to_i
p “13”.to_f
p “12”.to_r
p “13”.to_c

这里将字符串转换成整数、小数、有理数和复数。


p “Jane”.to_sym

将字符串变为符号标志。


v = “Ruby Python Tcl PHP Perl”.split
p v.class

使用字符串的split方法将字符串转换成数组。


$ ./stringconv.rb

12

13.0

(12/1)

(13+0i)

:Jane

Array


输出结果。




下面的小例子展示了数组哈希表的转换。


#!/usr/bin/ruby

h = {:de => “Germany”, :sk => “Slovakia”}
p h.to_a

a = [:de, “Germany”, :sk, “Slovakia”,
:hu, “Hungary”, :no, “Norway”]
p Hash[a]

这个例子的代码我们创建了一个哈希表并转换成数组,创建一个数组并转换成哈希表。


h = {:de => “Germany”, :sk => “Slovakia”}
p h.to_a

哈希表转换为数组使用to_a方法。


a = [:de, “Germany”, :sk, “Slovakia”,
:hu, “Hungary”, :no, “Norway”]
p Hash[
a]

将数组转换成哈希表。在这里的上下文中使用了星号操作符。这个风格来自Perl。它将一个数组分割成多个变量。


$ ./h2a.rb

[[:de, “Germany”], [:sk, “Slovakia”]]

{:de=>”Germany”, :sk=>”Slovakia”, :hu=>”Hungary”, :no=>”Norway”}


输出结果。


这章在教程覆盖了数据类型和它们的转换。




原文地址: http://zetcode.com/lang/rubytutorial/datatypes/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程5——对象

Ruby中的对象

在这章的教程中我们将简要的介绍下Ruby语言对象的概念。更多的内容将会在面向对象的章节中学习。由于Ruby的许多特性可以会使新手们迷惑,尤其是已经学习过其他编程语言的,因此才编写了这个关于对象的预备章节。


Ruby是一门面向对象的编程语言。这意味着我们可以在Ruby语言中使用对象。对于语言程序员来说Ruby程序就是一些字符流。这些符号就是Ruby的关键字、操作符、变量符号和字面量。从语言的角度来看Ruby程序是由对象组成的。这些对象在Ruby脚本程序执行进程来创建和修改。


有两种类型的对象:内置对象和自定义对象。内置对象是所有程序员都可以使用的预定义对象。它们由Ruby语言的内核或者变量库提示。自定义对象是由应用程序开发者在他们的应用程序域中创建的。


所有的对象都必须在使用之前创建。我们把创建对象叫做对象实例化。对象是由数据和方法组成,数据是对象静态的部分,方法是动态的形式。对象的修改以及与其他对象通信都是通过方法进行的。


#!/usr/bin/ruby
puts “Ruby language”

以上是一个简单的Ruby脚本。如果我们熟悉Pascal或者C之类的程序语言,我们可以看到一个名为puts的关键字或者是一个函数和一个字符串参数“Ruby language”


看起来有点不同,Ruby是一门纯面向对象语言。“Ruby language”确实是一个字符串,常见的一种数据类型。但是它也是一个对象。与所有的对象一样,我们可以调用它们的方法。这与其他的语言有点不同。puts是一个方法。方法是定义在一个对象中的函数。方法是不能独自存在的。实际上puts方法是Kernel模块的一部分。


#!/usr/bin/ruby
Kernel.puts “Ruby language”
Kernel.puts “Ruby language”.size

在以上的脚本中我们写了两行代码。


Kernel.puts “Ruby language”

Kernel是可以省略的,在第一个例子中我们调用puts方法就没有使用Kernel。这样可以少打些字节约时间。实际上它是对于Kernel.puts这个正式调用的简写。正如在C#中的Console.writeln和Java中的System.println。因此方法必须与一个对象相关联,或者如果是类方法就必须与类相关联。


Kernel.puts “Ruby language”.size

在这行代码中我们在终端上打印了“Ruby language”这个字符串的长度。对于有其他编程语言经验的程序员来说可能会困惑。在其他语言中字符串是一个原始数据类型,不能修改,并且没有自己的方法。在Ruby中字符串是一个完整的对象,有自己的方法。size方法其中一个,用于返回这个字符串的长度。


$ ./simple2.rb

Ruby language

13


以上是这个例子的输出。




接下来的例子我们看看整数。与字符串相似一个数字也是一个对象。


#!/usr/bin/ruby
puts 6.object_id
puts 6.even?
puts 6.zero?
puts 6.class

这个例子中我们定义了一个数字6。然后调用了一些这个数字的方法。


puts 6.object_id

这里6是一个对象,object_id是一个方法。这个方法返回了与这个对象相关联的id号。每个对象都拥有一个id号。如果我们要在对象上调用一个方法,就必须在他们之间加点号。


puts 6.even?
puts 6.zero?

这里我们对6这个对象调用了两个方法。如果一个数是偶数,那么even?返回true;如果一个数等于0,那么zero?返回true。注意这两个方法都是以问号能结尾的。在Ruby中约定了如果一个方法返回值是布尔类型,那么方法名以问号结尾。


puts 6.class

class方法告诉我们当前正在处理的这个对象是什么类型的。在这里6是一个Fixnum类型。


$ ./objectnumber.rb

13

true

false

Fixnum


以上是这个例子的输出结果。


创建对象


我们之前提到了在Ruby是对象使用之前必须先创建。对象可以被隐式创建或者显式创建。使用字面量符号创建对象是隐式创建;使用new关键字创建对象是显式创建。自定义对象都是显式创建的。自定义对象必须是从某个特定的类创建的。类是对象的模块,一个类可以创建多个对象。


#!/usr/bin/ruby
class Being
end

puts 67
puts “ZetCode”

s = String.new “ZetCode”
puts s

# n1 = Fixnum.new 67
# puts n1

b = Being.new
puts b

这个例子演示了Ruby中的对象创建。


class Being
end

这里是一个名为Being的对象模块。对象模块使用class关键字创建。自定义对象模块通常放在代码文件的顶部,或者分开放在另外的文件中。


puts 67
puts “ZetCode”

这两行代码我们用到了两个对象。Fixnum类型的67String类型的“ZetCode”67“ZetCode”是使用字面符号创建了。字面符号是一个文本,表示了一个类型的特定值。这两个对象是Ruby解释器幕后创建的。在源代码中Ruby的一些对象是使用他们特定的字面符号创建的。


s = String.new “ZetCode”
puts s

这是正式的方法创建字符串对象。它与之前的隐式创建是等价的。


# n1 = Fixnum.new 67
# puts n1

不是所有的内置对象都可以使用new方法来创建。以上代码就不能编译通过。Fixnum对象只能通过字面符号来创建。


b = Being.new
puts b

这里我们创建了自定义对象的一个实例。puts方法输出了这个对象的简短描述。


$ ./ocreation.rb

67

ZetCode

ZetCode

#<Being:0x9944d9c>


以上是输出结果。




继续介绍一些正式的对象创建。


#!/usr/bin/ruby

s1 = String.new “Ruby”
puts s1.size
puts s1.downcase

a1 = Array.new
a1.push 1, 2, 3
puts a1.include? 3
puts a1.empty?

r1 = Range.new 1, 6
puts r1.class
puts r1.include? 4

在这个例子中,我们创建三个内建对象并且调用了几个它们的方法。


s1 = String.new “Ruby”
puts s1.size
puts s1.downcase

创建一个字符串对象,并且调用了它的两个方法。size方法返回这个字符串的长度。downcase方法将这个字符串转为小写。


a1 = Array.new
a1.push 1, 2, 3
puts a1.include? 3
puts a1.empty?

这里创建了一个数组,并往其中添加了三个数字。然后调用了两个数组的方法。include?方法用于检查一个特定的值(在这个例子中是3)是否在数组内。empty?方法返回一个布尔类型的值表明这个数组是否为空。


r1 = Range.new 1, 6
puts r1.class
puts r1.include? 4

创建了一个Range类型的实例对象。它包含了1到6的数字。class方法返回这个对象的类型名称。在这个例子中include?方法检查数字4是否在这个范围内。


$ ./formal.rb

4

ruby

true

false

Range

true


这个例子的运行输出结果。


对象字面符


前面提到了一些内建对象可以使用对象字面符创建。接下来的例子展示了一些对象字面符。


#!/usr/bin/ruby

4.times { puts “Ruby” }

puts “Ruby”.size
puts “Ruby”.downcase

puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?

puts :name.class
puts :name.frozen?

puts (1..6).class
puts (1..6).include? 4

在这个例子中我们使用字面符创建了FixnumStringsArraysSymbolsRanges对象。


4.times { puts “Ruby” }

我们可以直接对一个整数字面符号调用方法。这行代码将在终端上打印4次”Ruby”字符串。


puts “Ruby”.size
puts “Ruby”.downcase

我们对使用字面符号创建的字符串调用了两个方法。


puts [1, 2, 3].include? 3
puts [1, 2, 3].empty?

这里我们使用字面符号创建了两个数组。然后使用include?方法检查一个特定的数字是否是这个数组里;使用empty?方法检查这个数组是否为空。


puts :name.class
puts :name.frozen?

使用以冒号开头的字面符号创建了符号对象,并且调用的它的两个方法。


puts (1..6).class
puts (1..6).include? 4

使用字面符号创建了两个范围对象,并且调用了他们的两个方法。class方法返回这个类的名称,include?方法检查给定的数字是否是这个范围内。


$ ./literals.rb

Ruby

Ruby

Ruby

Ruby

4

ruby

true

false

Symbol

false

Range

true


以上是例子的输出结果。


对象层级


在许多面向对象语言中对象是层级形式的。Ruby中也有对象层级。与树形层级相似,我们也有父对象和子对象。对象从它的父对象中继承数据和行为。层级的顶级是根对象,称为Object。在Ruby中每个对象都至少有一个父对象。也就是就每个对象都继承至Object对象的基类。


根据Ruby的官方文档,Object是Ruby的类层级的根。它的所有方法在所有的类中都是一样的,除非显示的覆盖了。


#!/usr/bin/ruby

puts 4.is_a? Object
puts “Ruby”.is_a? Object
puts [2, 3].is_a? Object
puts :name.is_a? Object
puts (1..2).is_a? Object

以上例子的代码展示了所有对象都是继承至根对象Object


puts 4.is_a? Object

使用is_a?方法检查一个数字是否为特定的类型,也就是说它是否继承至给定的对象类型。


$ ./mother.rb

true

true

true

true

true


所有方法都返回true,意味着所有的对象都继承至母类。




即使是基本的Ruby对象,它们的继承层级也可能是很复杂的。


#!/usr/bin/ruby

puts 6.class

puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum

puts 6.is_a? Bignum
puts 6.is_a? String

这个例子展示了小数字的继承层级。


puts 6.class

我们发现数字6的类型是Fixnum


puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum

以上这些代码全都返回true。数字6是Fixnum类型。从Ruby文档中我们发现另外四个对象是Fuxnum对象的父类。


puts 6.is_a? Bignum
puts 6.is_a? String

$ ./inheritance.rb

Fixnum

true

true

true

true

true

false

false


以上是输出结果。




这节的最后再举个例子展示下自定义对象的继承。


#!/usr/bin/ruby

class Being

def to_s
“This is Being”
end

def get_id
9
end
end

class Living < Being

def to_s
“This is Living”
end
end

l = Living.new

puts l
puts l.get_id
puts l.is_a? Being
puts l.is_a? Object
puts l.is_a? BasicObject

在这个例子中我们创建了两个对象。BeingLivingLiving对象继承至Being。第一个是父对象,第二介是子对象。


class Being

def to_s
“This is Being”
end

def get_id
9
end
end

这里定义了一个Ruby的自定义对象。定义内容是classend关键字之间。在定义里我们创建了两个方法。当puts方法接了一个对象作为参数时,会调用它的to_s方法。它通常返回这个对象的字符串描述。


class Living < Being

def to_s
“This is Living”
end
end

定义了一个Living对象,这个对象继承至Being对象。<操作符用于创建继承关系。to_s方法被覆盖了。


l = Living.new

我们创建了一个Living实例对象。自定义对象使用new关键字创建。


puts l

puts方法调用Living对象的to_s方法。只有当Living类没有定义to_s方法时,Being类的to_s方法才会被调用。


puts l.get_id

Living对象没有定义get_id方法。在这种情况下就检查它的父类是否有此方法。在这里Being有这个方法,并且被调用。


puts l.is_a? Being

这行将返回trueLiving对象是Being类型的,因为它继承至Being类。


puts l.is_a? Object
puts l.is_a? BasicObject

我们的Living自定义对象中,没有特别显示的指定与Object或者BasicObject的关系。然而这两行也返回true。这是因为在Ruby中所有对象都是自动设置为这两个对象的后代。这是Ruby解释器在幕后完成的。


$ ./custominher.rb

This is Living

9

true

true

true


以上是输出结果。


Ruby的顶级环境


Ruby中有一个特殊的对象引用到Ruby的顶级环境,这是定义在其他上下文之外的默认执行环境。顶级环境名字是main。它是一个Object类型的实例对象。以下main分配了一个局部空间,所有的局部变量有属于它。


#!/usr/bin/ruby

n1 = 3
n2 = 5

puts local_variables

Kernel.puts self
puts self.class

这是描述Ruby顶级环境的第一个例子。


n1 = 3
n2 = 5

我们定义了两个数字类型的变量,这些变量是顶级环境的局部变量。


puts local_variables

这里我们输出所有的局部变量。local_variablesKernel模块的一个方法,它包含了每个顶级环境的对象。


Kernel.puts self

self是Ruby的伪变量。它返回当前对象的接收者。这行将在终端打印“main”。这是顶级环境的名字。Kernel.puts代码部分的Kernel可以省略。完整的名字表示的puts方法属于Kernel模块。


puts self.class

这行打印了顶级环境的类型。我们得到顶级环境的对象类型。它是Object类型,Ruby类层级的根。


$ ./toplevel.rb

n1

n2

main

Object


这是这个例子的输出结果。n1n2是顶级环境所分配的局部变量。main是Ruby顶级执行环境的名字。最后Object是顶级环境的类型。




我们再展示另一个关于Ruby顶级环境的例子。


#!/usr/bin/ruby

@name = “Jane”
@age = 17

def info
“#{@name} is #{@age} years old”
end

puts self.instance_variables
puts self.private_methods.include? :info

puts info

我们展示的属于顶级环境的实例变量和方法。


@name = “Jane”
@age = 17

我们定义了两个实例变量。在Ruby中实例变量以@符号开头。实例变量属于指定的对象实例。在这里是属于Ruby的顶级环境。


def info
“#{@name} is #{@age} years old”
end

这里定义了一个方法。每个方法都必须属于一个对象。这个方法是属于顶级环境的。所有顶级环境的方法都是私有的。私有方法的访问是受制的。


puts self.instance_variables

instance_variables方法打印self的所有实例变量。


puts self.private_methods.include? :info

所有的顶级环境的方法都自动设为私有。private_methods返回该对象所有的私有方法。由于内容太多,我们就调用include?方法来检查info方法是否是其中一个。注意我们是通过符号名来引用info的。


$ ./toplevel2.rb

@name

@age

true

Jane is 17 years old


以上是例子的输出结果。


这章包含了Ruby对象的基本内容。




原文地址: http://zetcode.com/lang/rubytutorial/objects/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程4——变量

变量

在这章的教程中我们将详细介绍变量。


变量是保存数据的地址。每个变量都有唯一的一个名字,变量命名存在着一些约定。变量保存着数据对象,更确切的说变量是对数据所在的计算机内存地址的引用。每一个对象都具有一定的数据类型,内置的类型或者自定义的类型。Ruby属于动态语言,与Java、C或者Pascal之类的强类型语言不同,动态语言不用为变量定义确切数据类型,而是解释器在分配变量时决定它的类型。在Ruby中程序运行过程变量可以包含不同类型的不同值。



#!/usr/bin/ruby


i = 5

puts i

i = 7

puts i



变量是可变的,与常量不同,在程序运行过程中它们可以保存不同的值。在上面这个例子中有一个名为i的变量,首先赋值为5,然后又改为7。


命名约定


与其他语言一样,Ruby对变量标识符也有些命名约定。

Ruby是区分大小写的,这意味着ageAge是两个变量名。许多语言都是区分大小写的,但是BASIC例外。我们改变字符的大小写可以创建不同的变量,但是不推荐这种做法。



#!/usr/bin/ruby


i = 5

p i

I = 7

p I



这个例子定义了两个变量Ii,它们保存了不同的值。


./case.rb

5

7


以上是case.rb脚本的输出结果。




在Ruby中变量名可由字母数字和下划线组成。为了使解释器能简单的从字面上区分数字和变量,变量名不能以数字开头。变量名同样也不能以大写字母开头,在Ruby中以大写字母开头会被认为是常量。



#!/usr/bin/ruby


name = “Jane”

placeOfBirth = “Bratislava”

placeOfBirth = “Kosice”

favorite_season = “autumn”


n1 = 2

n2 = 4

n3 = 7


p name, placeOfBirth, favorite_season

p n1, n2, n3



在这个脚本中我们展示一些变量的命名。




变量名应该是有意义的。好的编程习惯是给变量取个具有描述性的名字,使得程序更加可读。


#!/usr/bin/ruby

name = “Jane”
place_of_birth = “Bratislava”
occupation = “student”

i = 5
while i > 0 do
puts name
i -= 1
end

这个脚本展示三个具有描述性的变量名。对于程序员来说place_of_birth比其他的名字如pob更具有描述性。在循环的时候通常选择一个比较简单的变量名。


印章(Sigils)


变量标识符可以以一些特殊的印章(Sigils)符号开头。印章(Sigils)是附加在标识符上的符号。在Ruby中变量印章(Sigils)表示了变量的作用域范围。在Perl中它是表示数据的类型。Ruby的变量印章(Sigils)符号有$@



#!/usr/bin/ruby


tree_name = “pine”

$car_name = “Peugeot”

@sea_name = “Black sea”

@@species = “Cat”


p local_variables

p global_variables.include? :$car_name

p self.instance_variables

p Object.class_variables



我们定义了四个不同作用域范围的变量。作用域表示了该变量可以被引用到的范围。我们使用了内置的方法来决定变量的作用域。



tree_name = “pine”



不包含印章(Sigils)符号的变量是一个局部变量。局部变量只在局部有效,如在方法、代码块、模块内。



$car_name = “Peugeot”



全局变量以$符号开头。它们在所有地方都是有效的。在程序中不要滥用全局变量。



@sea_name = “Black sea”



实例变量是以@符号开头。它只有在实例对象中才有效。



@@species = “Cat”



最后我们定义了一个类变量。它在所有属于这个类的实例中都有效。



p local_variables



local_variables是保存了当前环境下所有定义的局部变量的数组。



p global_variables.include? :$car_name



类似的,global_variables是保存的全部全局变量的数组。由于全局变量很多,我们就不将它们全部在终端上打印了。每次Ruby脚本启动时都会预定义一些变量。我们使用数组的include?方法来检查我们全局变量是否定义了。同时请注意我们引用变量是用的符号。(符号是以一个冒号开头)



p self.instance_variables



self伪变量指向了instance_variables方法的接收对象。这个例子中的接收对象是main,Ruby的顶级执行区域。



p Object.class_variables



最后我们获取所有的类变量数组。main是一个Object类的实例。


$ ./sigils.rb

[:tree_name]

true

[:@sea_name]

[:@@species]


以上是这个例子的输出结果,我们看到了变量的符号名。


局部变量


局部变量是只在Ruby源代码的局部区域有效。这个区域也称为局部作用域。局部变量是在Ruby的模块、方法、类中定义。


#!/usr/bin/ruby

def method1
x = 5
p x
end

method1

p x

我们定义了一个名为method1的方法,它有一个变量。这个变量是局部变量。这意味着这个变量只在这个方法内有效。我们只能在这个方法名到end关键字之间访问x变量。


def method1
x = 5
p x
end

以上定义了一个method1方法。在方法内部创建了一个局部变量x,然后将它的值打印到终端。



method1



方法被调用。



p x



我们试图在方法外部访问这个局部变量。这将导致NameError错误,Ruby解释器找不到这个标识符。


$ ./locals.rb

5

./locals.rb:11:in <main>’: undefined local variable
or method
x’ for main:Object (NameError)


以上是这个例子的输出结果。




以下例子是对前一个例子的简单修改。


#!/usr/bin/ruby

x = 5

def method1
x = 10
p x
end

method1

p x

我们定义了两个x变量。一个是在method1内部定义的,另一个是在外部定义的。他们是两个不同的局部变量,并不会相互冲突。


x = 5

我们创建了一个局部变量,它的值为5。这个变量的局部作用范围是main区域。它在method1内部是无效的。


def method1
x = 10
p x
end

method1内部创建了一个新的x局部变量,它的值是10.它存在于method1方法内部,在end关键字之后就会失效。


$ ./locals2.rb

10

5


以上是输出结果。




如果一个方法接收了参数,那么就会创建与这些参数相对应的局部变量。


#!/usr/bin/ruby


def rectangle_area a, b
puts local_variables
return a b
end

puts rectangle_area 5, 6

我们定义了一个方法,它接收两个值,然后返回这个矩形的面积。


def rectangle_area a, b
puts local_variables
return a
b
end

rectangle_area方法接收两个参数。它们是矩形的边长,然后我们计算它的面积。对应于标识符ab的两个局部变量将自动创建了。我们调用local_variables方法查看方法内部的所有局部变量。


puts rectangle_area 5, 6

这里我们给rectangle_area方法传了两个值。这两个值将会分配给在方法内部创建的两个局部变量。


$ ./parameters.rb

a

b

30


输出了三个结果。前两个是rectangle_area方法内部的局部变量名。第三个是面积的计算结果。




一个方法可以定义在另一个方法的内部。内嵌方法有它自己的局部变量。


#!/usr/bin/ruby

def method1

def method2

def method3
m5, m6 = 3
puts “Level 3”
puts local_variables
end

m3, m4 = 3
puts “Level 2”
puts local_variables
method3
end

m1, m2 = 3
puts “Level 1”
puts local_variables
method2

end

method1

在这个Ruby脚本中我们创建了三个方法。method2method3是内嵌方法。method2定义在method1内部,method3又定义在method2内部。每一个方法的局部变量仅在这个方法内是可访问的。


$ ./lms.rb

Level 1

m1

m2

Level 2

m3

m4

Level 3

m5

m6


从输出结果我们可以知道method1有两个局部变量m1m2。内嵌方法method2的局部变量有m3m4。最内部的方法method3的局部变量是m5m6




这一节的最后一个例子将展示一些局部作用域的示范。


module ModuleM
m1, m2 = 4

puts “Inside module”
puts local_variables
end


def method1
v, w = 3
puts “Inside method”
puts local_variables
end


class Some
x, y = 2
puts “Inside class”
puts local_variables
end

method1

t1, t2 = 7

puts “Inside toplevel”
puts local_variables

在这个例子我们分别在模块、方法、类和顶级环境中创建局部变量。local_variables是内核模块的一个方法,用于获取当前的所有局部变量。


module ModuleM
m1, m2 = 4

puts “Inside module”
puts local_variables
end

模块是一个方法和常量的集合。我们创建了两个局部变量m1m2


def method1
v, w = 3
puts “Inside method”
puts local_variables
end

method1方法中创建了两个局部变量vw


class Some
x, y = 2
puts “Inside class”
puts local_variables
end

Some类中创建了两个局部变量xy


t1, t2 = 7

最后我们为Ruby顶级环境创建两个局部变量。


$ ./locals3.rb

Inside module

m1

m2

Inside class

x

y

Inside method

v

w

Inside toplevel

t1

t2


输出结果展示了各个作用域的局部变量。


全局变量


全局变量可以在脚本的任何地方访问到。它们以$符号开头。

全局变量可能会引起很多程序错误,因此不鼓励使用全局变量。除非有原因要使用,否则建议使用局部变量。


#!/usr/bin/ruby

$gb = 6


module ModuleM
puts “Inside module”
puts $gb
end


def method1
puts “Inside method”
puts $gb
end


class Some
puts “Inside class”
puts $gb
end

method1

puts “Inside toplevel”
puts $gb
puts global_variables.include? :$gb

在这个例子中我们创建了一个全局变量$gb。这个变量在模块、方法、类和顶级环境都可以访问。全部变量$gb是所有的实体中都是有效的。


$gb = 6

创建全局变量$gb,它的值为6。


module ModuleM
puts “Inside module”
puts $gb
end

在一个模块定义中我们打印全局变量的值。


def method1
puts “Inside method”
puts $gb
end

在一个方法定义中我们打印全局变量的值。


class Some
puts “Inside class”
puts $gb
end

在一个类定义中我们打印全局变量的值。


puts $gb
puts global_variables.include? :$gb

在顶级环境下打印全局变量的值,并检查这个变量是否为全局变量。


$ ./globals.rb

Inside module

6

Inside class

6

Inside method

6

Inside toplevel

6

true


这个例子的输出结果确认了全局变量可以任何地方访问到。




当一个Ruby脚本启动时,它会访问多个预定义的全局变量。这些全局变量不认为有害并且能帮助完成一些常见的任务。


#!/usr/bin/ruby

p $LOAD_PATH
p $:

这个脚本显示了$LOAD_PATH这个全局变量。这个变量列出了require方法会搜索的所有目录。$:$LOAD_PATH的缩写。

更多的全局变量会在本章的变量预定义这节中介绍。


实例变量、类变量


在这节将简要的介绍下实例变量和类变量。它们将会在面向对象那一章详细介绍。

实例变量是属于具体某个对象实例的变量。每个对象都有它自己的变量。实例变量以@符号开头。类变量属于特定某个类的。这个类所创建的对象实例共享这个类的类变量。类变量以@@符号开头。


#!/usr/bin/ruby

class Being

@@is = true

def initialize nm
@name = nm
end

def to_s
“This is #{@name}”
end

def does_exist?
@@is
end
end

b1 = Being.new “Being 1”
b2 = Being.new “Being 2”
b3 = Being.new “Being 3”

p b1, b2, b3

p b1.does_exist?
p b2.does_exist?
p b3.does_exist?

我们创建一个自定义Being类。这个Being类有一个类变量和一个实例变量。


class Being
@@is = true

@@is是一个类变量。这个类变量被所有Being类的实例所共享。这个例子的逻辑是判断是不是Being


def initialize nm
@name = nm
end

initialize方法是构造函数。这个方法在对象被创建时调用。用于创建@name实例变量。


def to_s
“This is #{@name}”
end

当这个对象作为打印方法如p或者puts的参数时to_s method方法会被调用。这个方法返回这个对象便于人类阅读的描述内容。


def does_exist?
@@is
end

does_exist?返回类变量。


b1 = Being.new “Being 1”
b2 = Being.new “Being 2”
b3 = Being.new “Being 3”

创建Being类的三个实例对象。每个对象拥有不同的名字。这个名字存储在实例变量中,对于每个对象它是唯一的。名字将会在to_s方法中使用,用于返回一个对该对象的简短描述。


p b1, b2, b3

这个方法将刚创建的三个对象作为参数,它将调用每个对象的to_s方法。


p b1.does_exist?
p b2.does_exist?
p b3.does_exist?

最后我们调用每个实例对象的does_exist?方法。这三个方法会输出相同的结果,因为这个方法返回的是类变量。


$ ./icvars.rb
This is Being 1
This is Being 2
This is Being 3
true
true
true

以上是这个例子的输出结果。前三条信息是唯一的,因为这个字符串是存储在实例变量中的。true值是类变量,它被调用的三次。


环境&命令行变量


可以用ENV常量来访问环境变量。它是一个Ruby的hash对象。每个环境变量都是ENV这个hash对象的键值。


ARGV常量存储了命令行的参数值。它们是在脚本启动时传递的。ARGV是一个数组,参数以字符串存储。SARGV的别名。


ENVARGV都是全局常量。


#!/usr/bin/ruby

ARGV.each do |a|
puts “Argument: #{a}”
end

这个脚本我们通过循环遍历打印了ARGV的每个值。


$ ./commandline.rb 1 2 3

Argument: 1

Argument: 2

Argument: 3


我们给了三个命令行参数。它们在终端上各打印了一行。




接下来的例子介绍了处理环境变量。


#!/usr/bin/ruby

puts ENV[‘SHELL’]
puts ENV[‘LANG’]
puts ENV[‘TERM’]

这个脚本在终端上打印了三个环境变量的值。这些变量值的内容依赖于我们操作系统的系统设置。


$ ./environment.rb

/bin/bash

en_US.utf8

xterm


以上是一个输出例子。


伪变量


在Ruby中有一些变量被称作伪变量。它们不同于常规的变量,不能给它们设置值。

self是当前方法的接收者。nilNilClass的唯一实例,它代表值不存在。trueTrueClass的唯一实例,它代表布尔真。flaseFalseClass是唯一实例,它代表布尔假。

truefalse是布尔数据类型。从另一个角度来看他们是特殊的类实例,这是因为在Ruby中一切皆对象。


#!/usr/bin/ruby

p self
p nil
p true
p false

p self.class
p nil.class
p true.class
p false.class

这是一个伪变量的例子。我们打印所有的伪变量,然后再看它们的类名。


p self

在当前上下文self伪变量返回的是main执行的上下文。


$ ./pseudo.rb

main

nil

true

false

Object

NilClass

TrueClass

FalseClass


以上是例子的输出结果。




在本节的第二个例子,我们将进一步分析self


#!/usr/bin/ruby

class Some
puts self
end

class Other
puts self
end

puts self

之前我们说过,self是对当前方法接收者的引用。以上例子展示了三个不同的接收者。


class Some
puts self
end

这个接收者名为Some


class Other
puts self
end

这是另一个接收者,名为:Other


puts self

第三个接收者是Ruby顶级环境。


$ ./pseudoself.rb

Some

Other

main


以上是例子的输出结果。




本节的最后一个例子展示了另外三个伪变量。


#!/usr/bin/ruby

if true
puts “This message is shown”
end

if false
puts “This message is not shown”
end

p $name
p $age

上面的例子展示了truefalsenil伪变量。


if true
puts “This message is shown”
end

true用于布尔表达式中。这条消息总是会打印的。


if false
puts “This message is not shown”
end

这条消息永远不会打印。这个条件不成立。这个布尔表达式总是会返回一个负值。


p $name
p $age

如果全局变量没有初始化就引用,那么它们就会包含一个nil伪变量。这代表值不存在。


$ ./pseudo2.rb

This message is shown

nil

nil


以上是pseudo2.rb脚本的输出结果。


预定义变量


Ruby中有很多预定义的全局变量。这是继承到Perl,Ruby受Perl的影响很大。Ruby脚本启动之后就可以访问这些变量了。接下来有些例子展示预定义变量。


#!/usr/bin/ruby

print “Script name: “, $0, “\n”
print “Command line arguments: “, $
, “\n”

puts “Process number of this script: #{$$}”

以上使用了三个预定义变量。$0$$$$0存储了当前脚本的名字。$存储了命令行参数。 $$存储了当前脚本程序的PID。


$ ./predefined.rb 1 2 3

Script name: ./predefined.rb

Command line arguments: [“1”, “2”, “3”]

Process number of this script: 3122


以上是一个输出例子。




$?全局变量存储了最后一个子进程的退出状态。


#!/usr/bin/ruby

system ‘echo “Ruby”‘
puts $?

%x[exit ‘1’]
puts $?

我们执行两个子进程,然后使用$?查看它们的退出状态。


system ‘echo “Ruby”‘
puts $?

使用system方法启动一个子进程。它是一个bash的echo命令,用于在终端输出消息。


%x[exit ‘1’]
puts $?

第二个情况是使用状态1执行bash的exit命令。这次我们使用%x 操作符,用于执行一条被分隔符所选择的命令。


$ ./predefined2.rb

Ruby

pid 3131 exit 0

pid 3133 exit 1


第一个子进程退出状态为0,第二为1。




$;变量存储了字符串split方法的默认分隔符。


#!/usr/bin/ruby

str = “1,2,3,4,5,6,7”
p str.split

$; = “,”
p str.split

我们使用$;变量来控制字符串的split方法是如何分隔的。这个方法接收一个参数,用于确定字符串应该何处分隔。 如果这个参数省略了,那么将会使用$;的值。


$; = “,”
p str.split

我们为$;分隔符指定个值。当split方法没有传递参数时,$;的值将会被使用。


$ ./predefined3.rb

[“1,2,3,4,5,6,7”]

[“1”, “2”, “3”, “4”, “5”, “6”, “7”]


在第一种情况下字符串没有被分割,第二种情况下字符串正确的被分割了。




最后我们展示三个用于正则表达式的全局预定义变量。


#!/usr/bin/ruby

“Her name is Jane” =~ /name/

p $p $&amp; p $' </code></pre> <p>当我们对字符串使用<em>=~</em>保用符时,Ruby设置了一些变量。<em>$&amp;</em>变量设为最后一个匹配该正则式的内容。<em>$设为$&之前的内容.$’$&之后的内容。


$ ./predefined4.rb

“Her “

“name”

“ is Jane”


以上是这个例子的输出结果。


在这一章的教程中我们深入的学习了Ruby的变量。




原文地址: http://zetcode.com/lang/rubytutorial/variables/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial

[翻译]Ruby教程3——基础

基础

在这一章我们将覆盖Ruby语言的基本程序概念。我们展示一些非常基础的程序,使用到了变量、常量和基本的数据类型。从终端读取、写入内容;我们将会介绍变量的插值(interpolation)。


我们从一个非常简单的代码例子开始。



#!/usr/bin/ruby

puts “This is Ruby”



这个简单的脚本会在终端打印“This is Ruby”信息。



#!/usr/bin/ruby



这用于执行这个脚本的Ruby解释器的路径。



puts “This is Ruby”



puts是Ruby的关键字,用于将它的参数打印到终端上。在这个例子中它的参数是一个被双引号分隔的字符串。


$ ./first.rb

This is Ruby


以上是这个例子的输出结果。




我们可以从终端读取值。



#!/usr/bin/ruby

print “What is your name? “

name = gets
puts “Hello #{name}”



第二个程序将会从终端读取一个值并输出。



print “What is your name? “



print关键字是将参数打印到终端上。printputs的不同在于print不同自动换行,而puts会自动换行。



name = gets



这里我们将从用户输入中读取值并将其保存在name变量中。gets是一个方法用于从终端读取一行内容。它是一个内置的方法。



puts “Hello #{name}”



在这行代码我们展示变量的插值。变量插值(Variable interpolation)是将字符串字面量中的变量替换成它真实的值。变量插值(interpolation)的其他说法是:变量置换和变量扩展。



$ ./name.rb

What is your name? Jan

Hello Jan



以上是第二个程序的输出结果。




Ruby代码可以从命令行运行。这个是借鉴至Perl的单行方式,小块代码执行小任务。



$ ruby -e “puts RUBY_VERSION”

2.0.0



-e参数告诉Ruby要执行的代码从命令行中给定,而不是Ruby代码文件。我们例子将在终端输出Ruby解释器的版本号。

译注:原文的作者使用的是ruby 1.9.3,而如今ruby最新版已经是2.0.0了。




Ruby解释器有一个-c选项用于检查代码的语法。如果启用了的话,代码将不会被执行。如果代码没有语法错误,那么将会打印“Syntax OK”



#!/usr/bin/ruby

class Being end

m = Test.new

p m



以上例子中有一个语法错误。如果我们将classend关键字放在同一行,那么我们就必须还要使用分号字符(;)。


$ ruby -c syntax_check.rb

syntax_check.rb:3: syntax error, unexpected keyword_end, expecting ‘<’ or ‘;’ or ‘\n’

syntax_check.rb:6: syntax error, unexpected $end, expecting keyword_end


发现了语法错误。如果我们在Being的后面加上分号,错误提示消息就会消失。


命令行参数


Ruby程序可以从命令行接收参数。当我们执行程序时参数接在程序名的后面。



#!/usr/bin/ruby

puts ARGV



接在文件名后面的有效命令行参数被Ruby程序保存在全局数组ARGV中。


这里我们将所有的命令行参数打印到终端上。


$ ./args.rb 1 2 3

1

2

3


我们提供了三个数字作为命令行参数,并且它们都打印到终端上了。




在下面的例子中我们将打印所有的参数还有脚本名。



#!/usr/bin/ruby

puts $0

puts $



$0是全局变量,包含被执行的脚本的名称。在Ruby中全局变量是以$字符开头。$是另一个全局变量。它与ARGV是相等的,包含了命令行的所有参数。


$ ./args2.rb Ruby Python Perl

./args2.rb

Ruby

Python

Perl


args2.rb脚本接收了三个参数。脚本名和这三个参数都打印到终端上了。


变量和常量


变量是保存数据的地址。变量包括名称和数据类型。数据类型表示了数据的不同类型。整数、字符串和浮点数都是数据类型。Ruby是一门动态语言,这意味着我们不需要(也不能)定义一个变量的确定类型。而是Ruby解释器在分配变量时决定它的数据类型。此外,在程序运行时一个变量还可以包含不同类型的不同值。这与Java、C或者Pascal之类的强类型语言不同。与变量不同,常量(应该)保持它的值。一旦初始化了就不能修改。然后在Ruby中是有可能修改常量的值,这种情况下会出现一个警告。



#!/usr/bin/ruby

city = “New York”

name = “Paul”; age = 35

nationality = “American”


puts city

puts name

puts age

puts nationality


city = “London”


puts city



在上面的例子中我们使用了四个变量。



city = “New York”



我们给city变量分配了一个字符串。这个变量是动态创建的。



name = “Paul”; age = 35



创建两个变量。我们可以将两条语句放在一行里。然后为了提高可读性应该每行一条语句。



puts city

puts name

puts age

puts nationality



将变量的值打印到终端。



city = “London”



city变量分配新的值。


$ ./variables.rb

New York

Paul

35

American

London


这是以上例子的输出。




之前我们已经介绍过常量了,它一直都保存着一个值。但是不同于其他语言,Ruby没有对这个进行强制限制。



#!/usr/bin/ruby


WIDTH = 100

HEIGHT = 150


var = 40

puts var


var = 50

puts var


puts WIDTH

WIDTH = 110

puts WIDTH



在这个例子中定义了两个常量和一个变量。



WIDTH = 100

HEIGHT = 150



在Ruby中常量是以大写字母开头的。通常是将所有字母都大写。



var = 40

puts var


var = 50



定义并初始化一个变量。随后又给它分配了一个新的值,这是合法的。



WIDTH = 100



我们给常量分配了一个新的值。常量一旦创建就不应该改变,否则它就不意味着是一个常量。Ruby解释器将会给出一条警告。


$ ./constants.rb

40

50

100

./constants.rb:13: warning: already initialized constant WIDTH

110


以上是脚本的输出。


变量插值


变量插值(interpolation)是将字符串中的变量替换成它的值。变量插值的其他说法是:变量置换和变量扩展。



#!/usr/bin/ruby


age = 34

name = “William”


puts “#{name} is #{age} years old”



在Ruby中字符串是不可变的。我们不能修改一个已存在的字符串。变量插值是字符创建时进行的。



age = 34

name = “William”



定义两个变量。



puts “#{name} is #{age} years old”



字符串是被双引号所引起来。当我们将变量名放在#{}字符之间时,这个变量会被它的值所篡改。


$ ./interpolation.rb

William is 34 years old


以上是这个例子的输出。


这一章覆盖了Ruby语言的一些基础。




原文地址: http://zetcode.com/lang/rubytutorial/basics/

翻译:龙昌 admin@longchangjin.cn

完整教程:https://github.com/wusuopu/Ruby-tutorial