这个简单的教程,可以帮助初学者了解 Ruby 的基本语法。
如果你还没有安装 Ruby,参考下 the environment setup page for instructions
Ruby 的历史
许多人以为 Ruby 是一门年轻的语言,但是实际上 1994 年就发布了,作者是一个叫 Matz 的开发者。Matz 是个痴迷于编程语言的极客,非常喜欢 Perl。他创立 Ruby 初衷就是希望 Ruby 跟 Perl 一样灵活和强大,同时语法上更加丰富,代码更具有可读性。
Ruby 很快就有了一批追随者。但是在 2000 以前,所有的 Ruby 文档都是日语的。所以,如果你想学习 Ruby,还得先学习日语。大卫.托马斯是个敏捷编程的先驱,迷恋上了 Ruby,决定写 Ruby 的文档。他写了一本颇有影响力的书,叫 The Pickaxe (镐头),叫这个名字是因为封面上的那张图。这本书打开了英语世界了解 Ruby 的窗口。
英语世界里知道 Ruby 的人开始多了起来,但是增长十分缓慢。直到系统管理员开始用 Ruby 写维护和「胶水」脚本-以前都用 Perl 写的,Ruby 这才开始流行起来。从 2000-2005 年美国的
Ruby 社区已经有了几百个会员。
2004-2005 年,芝加哥一个叫 37signal 的公司招了一个年轻的程序员开发一款 Web 应用。他们只关心客户端的设计和功能性,至于实现方面,则给了这个年轻的程序员以极大的自由发挥的空间。彼时,主要的 Web 技术还是 Perl CGI,PHP,JSP,ASP 等。这些语言或多或少都有痛点。所以大卫,现在我们都叫他 DHH,开创了一条新的道路。
他使用 Ruby 完成了这款应用。除了依赖核心库和少量的帮助库,差不多其他都是靠自己完成的。他和 37signal 都为这款应用做开发工作,这款应用现在叫 Basecamp。
Basecamp 完成之后,DHH 就从应用里提取出一种 Web 框架。不同于 Java 和 .NET 从上至下的框架。Rails 完全都从真实的案例中提取出来的。它遵循约定俗成胜于配置的原则,使得通用的问题更容易解决。
Rails 取得了巨大的成功。之后 Rails 驱动了 Ruby 社区的巨大增长。现在你可以在亚马逊上买到十几本关于 Ruby 的书,全世界有几百场有关 Ruby 的会议,数以千计的人成为专业的 Ruby/Rails 开发者。
如果你想学 Rails,得先学学 Ruby,走起…
1. 简要说明和解释器
Ruby 是一种解释性的语言,那就意味着他不直接跑在处理器上。它通过虚拟机来运行,这种方式的好处就是一次编写,在各种不同的操作系统和硬件平台上都可以运行。
Ruby 程序不能自己运行,你要先载入虚拟机。在虚拟机里有两种方式执行 Ruby 代码:IRB 和 命令行。
通过命令行运行 Ruby
把 Ruby 代码写到一个文件里是比较常用靠谱的方法,文件可以备份,传输,加到源码控制等等。
一个简单的 Ruby 例子
我们创建一个文件,命名为 my_program.rb 如下
|
|
然后我们这样运行程序
|
|
当你运行 ruby my_program.rb 的时候,你实际上就已经载入虚拟机开始运行 my_program.rb 文件了。
从 IRB 运行程序
Ruby 是第一个普及交互式脚本的语言:读取-计算-输出-循环。把它想成一个计算器,当你输入一个完整的指令,IRB 会执行并且计算出结果,输出给你。
IRB 最好用的就是作为一个实验的草稿箱。许多开发者开发程序的时候都开着 IRB 窗口。用它来回忆方法的使用或者调试代码。
我们也开始用 IRB 来做实验了。打开命令行,输入 irb.
2. 变量
编程就是创造抽象的概念。为了创造这种抽象的概念,我们要给事物一个名字,而变量就是为数据命名的。
在有些编程语言中,变量只能保存特定类型的数据。但是 Ruby 不需要,它有非常灵活的类型系统,任何一个变量可以保存各种数据类型。
创建变量,并且赋值
在有些编程语言中,你需要先声明变量,然后才能赋值。Ruby 的变量是在你赋值的时候自动创建的,不需要指定。来看个例子
|
|
a = 5 这个语句创建了变量 a,并且将 5 存储到 a 里。
从右边开始看
日常阅读时,我们从左往右读,所以很自然的读代码也这样。但是当用 = 赋值的时候,Ruby 实际上是先执行等号右边的。举个例子:
|
|
10 + 5 先被执行,然后将结果赋值给变量 b。
灵活的变量赋值
Ruby 的变量可以保存任何类型的数据,甚至能够改变保存的数据类型。比如:
|
|
先给 c 赋值了一个 20,然后再将字符串 “hello” 赋值给 c,c 的值就变成了 “hello”。
变量命名规则
虚拟机对大多数的 Ruby 变量命名都有格式要求,一般
- 小写字母开头,(下划线也可以,但是不常用)
- 没有空格
- 不能包含特殊字符,比如 $, @, & 等。
除此之外,还有一些 Ruby 程序员通用的命名风格:
- 每个单词都小写并且通过下划线连接
- 根据变量的内容命名,而不是内容的类型
- 不能太短
好的变量名应该像 count,students_in_class,或者 fisrt_lesson.
不要像下面那样写:
- studentsInClass - 用驼峰命名法,而不是下划线连接,应该像 students_in_class
- 1st_lesson - 变量不能以数字开头,应该是这样:fisrt_lesson
- students_array - 这个命名格式是对的,但是名字代表了数据的类型,所以也不规范,因该是 students
- sts - 变量名太短了,用 students 就好了
练习
使用交互命令行 IRB,试试那些名字是好的,那些是非法的,哪些是合法但是不符合 Ruby 风格的。
- time_machine
- student_count_integer
- 3_secitons
- top_ppl
3. 字符串
现实生活中,英语 string 代表绳子,绳子可以用来捆东西。程序中的字符串跟现实生活中的绳子没有关系。
字符串用来存储数字和字母的集合。它们既可以是单个字母 “a” 也可以是一个单词 “hi” 或者像一个句子 “hello world!”
字符串的书写
一个 Ruby 的字符串以引号开头,可以包含 0 个或者多个字母、数字、连接符,再以引号结尾。最短的字符串是空字符串 “” 。一个字符串包含一段文字甚至几页文本都不足为奇。
子串
有时候,我们需要从一个字符串中拿出一部分,这个就叫子串。比如
|
|
正书和负数的位置
字符串中的字符通常都有正数数字,从 0 开始。以 “Hi” 为例,第 0 个位置是 “H” “i” 是第一个位置
我们用开始和结束符号来取出子串,比如 greeting[0..4] 代表从第 0 个位置到第 4 个位置的字符,共 5 个字符。
Ruby 解释器处理负数的方法是从字符串的最后开始截取,”Hi” 字符串的 “i” 是第 -1 个位置,”i” 是第 -2 个位置。
那么如果一个字符既有正数位置,又有负数位置,该如何选择呢?如果用正数很容易理解,但是如果你要一个字符串的最后几位(像 “What’s the last character of this string?”),这时候就用负数位置。
通用的字符串方法
我们在 IRB 里试验一下通用的几个字符串方法
- length
length 方法告诉你一个字符串里有多少字符,包括空格:12342.1.2 :012 > greeting = "hello world"=> "hello world"2.1.2 :013 > greeting.length=> 11
试试计算一下你名字的长度
- split
你经常需要把字符串切分成几个部分。比如,你在字符串里存了一个句子,现在想分割成单词:
|
|
.split 方法返回一个数组。他通过空格来分割字符串。
- .split 使用参数分割
有时候你想要不只是通过空格分隔,而是通过特定的字符。那么 .split 方法可以加参数来分割,比如1234562.1.2 :018 > numbers = "one,two,three,four,five"=> "one,two,three,four,five"2.1.2 :019 > numbers.split=> ["one,two,three,four,five"]2.1.2 :020 > numbers.split(",")=> ["one", "two", "three", "four", "five"]
调用第一个 split 方法时,因为没有空格,返回了一个整个字符的数组。在第二个方法中,我指定了 ,
作为分隔符,得到了一个包含五个单独单词的数组。
.sub 和 .gsub
这两个方法用来替换字符串。类似于 word 中的查找和替换。.sub 是 substitute 的缩写,代表单词替换。.gsub 代表全局替换,替换所有符合的字符。
|
|
连接字符串和变量
我们常常需要将变量和字符串连接在一起。比如,以这样一个字符串开始"Good morning, Frank!"
当我们在 IRB 里输入这个字符串的时候,还是返回这个字符串。要是写程序的话,我们希望程序能够跟他们各自的名字交互,而不只是 “Frank”.
那我们就需要将字符串和变量连接在一起。有两种方式可以实现
字符串的级联
最简单的方法叫做字符串的级联,用 + 将两者连接起来即可。
|
|
第一行我们命名了一个变量保存名字。第二行我们打印出了 “Good morinng” 和变量 name 以及 “!” 字符串。
字符串插值
第二种方法是用字符串插值,将数据插入到字符串中。
字符串插值能在双引号包裹的字符串中使用。在字符串中我们使用 #{} 表示插值。 我们可以把变量和将被赋值的代码,转换成字符串,然后输出。之前的例子可以用插值重写成这样:
|
|
如果你比较一下输出,就可以看到结果是一样的。插值的风格可以输入更少的字符,打开/关闭引号的频次更低,既然如此,那就忘了加号吧。
在插值中执行代码
你可以在插值的括号中放入 Ruby 代码和计算符,就像这样。
|
|
modifier * 3 + mood 先被计算,然后计算结果再插入到字符串中输出。
4. Symbols
Symbols(符号)比较难解释,他是介于字符串和数字之间的一种类型。符号很好认,都是以冒号开头,后面跟字母,比如 :flag 或者 :best_friend.
如果你是新手
如果你是编程新手,可以把符号想象为字符串的精简,没有很多方法和字符串插值。比较一下一个字符串支持的方法和符号的区别
|
|
如果你是个有经验的程序员
如果你是个有经验的程序员,可以把符号当成一种 “命名整形”。符号实际引用的值不重要,我们要关心的是在虚拟机里任何指向这个值都会返回同样的值。符号是在一个全局符号表里定义的,他们的值不会改变。
5. Numbers
Number 有两种基本的类型:integers (整数)和 floats (浮点型,有小数点的)。
Integers 对于你和计算机来说,都是相对容易处理的。你可以用常用的运算符操作,包括 +, -, /, *. Integers 有许多方法可以帮你做数学运算。可以通过 5.methods 来查看。
重复指令
其他语言里常用的重复模式就是 for 循环,通常用户重复一条指令很多次。比如在 JavaScript 中可以这样写:
|
|
For 循环用的比较多,但是这种代码不具备可读性。因为 Ruby 中的 integers 是对象,有方法。其中一个方法就是 time 重复某个指令若干次。用 Ruby 重写上面的循环大概是这样的。
|
|
这里例子里我们再用的 times 方法我们叫块。我们会在下一个部分讨论。在 IRB 里运行这个例子看看会发生什么。
6. Block (块)
块是 Ruby 里非常强大的概念,经常被用到。可以把它们看成是一系列说明的方法,可以被用到各个地方。
Blocks 的开始和结束
你刚刚已经看过了 integer 里使用 .times 方法
|
|
块以 do 开头,以 end 结尾。do/end 总是合适的。
括号里的块
当块只包含一个简单的指令,我们经常用 {} 来替代块的开始和结束
5.times{puts "Hello,world"}
块传递给方法
块有什么实际的用处呢?他们可以作为参数传递给方法调用。比如,我们刚才调用 5.times,Ruby 不知道我们想把什么样的语句执行 5 次。当我们传递块过去的时候,就想说 “我想让你每次都运行这个语句”.
很多方法都接受块传递。像刚才在字符串部分看到的 .gsub 方法就接受块传递。
|
|
注意下那句 Found an E! 出现了三次,因为有 3 个 e 在字符串里。
为什么结果是 "sntnv",留给你思考。
块参数
我没你经常遇到块里面语句需要引用我们现在使用的值,当我们写块的时候能够通过通过管道符认出块参数
|
|
块参数的取值取决于我们多调用的方法。这里例子里,我们调用的是 number 的 times 方法。先实验一下上面的语句,观察输出结果,然后再试试下面这个:
|
|
当调用 String 的 .gsub 方法,试试下面的代码(用括号):
你就会看到 .gsub 方法使用快语句的结果替换了原始的字符。
7. Arrays(数组)
我们写程序的时候,经常会需要处理一组数据的集合。在这一部分,我们就来看看最常用的数据的集合 —— 数组。
可视化的模型
数组是一个数字索引的列表。想象一下,在一张白纸上画三个小盒子:
|
|
你可以根据他们从左到右的位置算出每一个
|
|
然后把字符串放进每个盒子:
|
|
这个数组有三个元素。Ruby 的数组可以增加或者减少。通常都在数组的末尾增加元素。
|
|
想一想为什么最后一个数组的位置总是比数组里的元素少一位。
如果你请求数据里位置为 2 的元素,你会得到一个 “Dinner”。请求数组里的最后一个元素,会得到 “Dessert”。
代码里的数组
下面就是在 Ruby 代码里运行同样的模型。
|
|
观察一下上面的情况…
- 数组的元素以逗号分割,用中括号括起来
- 用两个小于号
<<往数组里添加元素 - 通过中
[]加元素的位置来取元素 - 还有一些方法方便区数据,比如
.last
通用的数组方法
数组还可以做很多酷的事情。举几个例子:
.sort 方法的解释
.sort 方法会返回一个重新排序后的新数组。如果数据里的元素是字符串,那么就会以字母表里的顺序。如果是数字,就会返回升序排列后的数组。下面试一试
|
|
你可以用 sort 方法重新对元素排序,你可以用 each 方法循环访问每一个元素。你可以通过 join 方法将他们连接成字符串。你可以通过 index 方法找到某个元素内存中的地址。你也可以通过 include? 方法判断该元素是否在数组里。
当我们需要一个有序的列表时,就可以用数组。
试试其他的
eachcollectfirstandlastshuffle
更多详细的说明,可以参考文档:Arrary
8. 哈希
哈希也是一组数据的集合,跟数组不同的是,哈希里的数据没有顺序,每个数据都有一个名字。跟冰箱很类似的。如果我们从冰箱里找东西,我们其实并不关心它在哪一层。顺序不重要。相反,我们是用名字来分类东西。比如苹果的数量是 3 个,橘子的数量是 1 个。胡萝卜是 12 个。在这种情况下,我们就用到了哈希。
键值对
哈希是无序的集合,数据是以键值对的形式存储。哈希的语法相对复杂,需要一点时间习惯。
|
|
key 可以看作是地址,value 就是存储在地址的数据。在 produce 这个哈希中,我们有 "apples" 和 "oranges" 这两个键,值就是 12 和 3。创建哈希时,键和值是通过 => 这个字符连接的,我们把这个字符叫做 rocket(火箭?)。所以哈希是大括号 { 开始,零个或者多个字符组成键,=> 和值,通过逗号分割,最后以大括号结尾 "}" 。
试试下面几个操作
|
|
第一行我们为这个哈希加了一个新的值。因为这个 "grapes" 键不在原始的哈希里,所以也给他加了一个值 221。键在哈希中必须是唯一的,所以当我们使用相对的语法对 produce["oranges"] 操作时, oranges 这个键已经存在,但是他的值会被 6 代替。 keys 和 value 方法会分别给出键值对中的另外半部分。
简单的哈希语法
实际编码中,我们经常使用符号来代替哈希中的键。当所有的键都变为符号的时候,那么哈希的语法看起来就简单一些了。
|
|
仔细观察你就会注意到键是冒号结尾,而不是开头,即便如此,这些也是符号。1.9 以上版本的 Ruby 都支持这种简单的语法。可以在控制台里用 ruby -v 看看你现在使用的 Ruby 版本。
9. 条件表达式
条件表达式的值是 ture 或者 false。最常见的条件操作符是 ==, >, >=, <, <=。
有些对象的方法也可以返回 true 或者 false,他们用的也是条件表达式。比如,每个对象都有一个方法 .nil? 当为 true 的时候,对象为 nil. 数组也有一个方法叫 .include? 当返回 true 的时候,说明该数组里包含这个元素。在 Ruby 语言的惯例中判断一个方法返回 true 还是 false 的时候,都会有一个 ? 结尾。
条件分支/语句
我们为什么要用条件声明?很多时候要进行条件控制,特别是 if/elsif/else 结构。我们来写个例子:
|
|
我们看一下这个 water_status 这个方法的值分别在 5,7,8,9 时候的取值。
|
|
理解这个执行的流程
当 minutes 是 5 的时候,先判断是不是小于 7, 是的,所以打印出 The water is not boiling yet. .
当 minutes 是 7 的时候,7 不小于 7,往下执行。等于 7,打印出 It's just barely boiling.
当 minutes 是 8 的时候,先判断 8 是不是小于 7,不是,再判断 8 是不是小于 7,也不是,继续往下走,8 是不是等于 8,是的,所以打印出 It's boiling!.
当 minutes 是 9 的时候,同理,执行最后一条语句 Hot! Hot! Hot!.
if 语句几种可能的情况
if 语句有下面几种…
- 只有一个 if 语句,当语句为 true 时,才会被执行
- 一个或多个 elsif 语句,当语句为 true 时,才会被执行
- 没有或者只有一个 else 声明,当 if 或者 elsif 都为 true 才会被执行。
if/elsif/else 结构里只有其中一项是可以被执行的。如果 if 为真,Ruby 直接忽略后面的 elsif 。一次只执行一个块,就是这样。
等于 vs 赋值
在写条件语句时,最容易犯的错误就是搞不清 = 和 == 的区别。
=是赋值,意思是把它左边的值放入到右边。是陈述,不是疑问==是疑问,意思是左边的这个东东跟右边是否相等,是疑问,不是陈述
你也可以用逻辑操作符跟条件语句连接。最常看到的就是逻辑与和逻辑或,分别可以用 && 和 || 表示。
10. Nil & Nothingness
什么是不存在?不存在是不是只在外太空?事实上,当我们认为不存的时候,是不是仅仅只是某种事物的抽象呢?好吧,这个词儿太哲学了…
在 Ruby 中nil 代表值不存在的。
你有 3 个鸡蛋,吃了 3 个鸡蛋,你可能认为你就啥也没有了。但是仅就鸡蛋而言,你有 0 个。0 是一个数字,并不是不存在。
如果你有一个字符串 “hello”,然后删除了 “h”,”e”,”l”,”o” 字符,你以为你啥也没了,但是实际上你还有个 “”,空字符串。这也不是不存在。
nil 就是 Ruby 里关于不存在的概念。当你请求一个不存在的事物时,它就出现了。我们创建了一个只有 5 个元素的数字,但是你非得请求数组的第六个元素,那 Ruby 就给我们一个 nil。这不是说第六个就是空白,他跟数字不一样,就是不存在- nil。
你在写 Ruby 时遇到的大部分错误都跟 nil 有关系。你以为出了问题,想尝试解决,但你不能解决一个不存在的问题,所以 Ruby 会给出错误。
11. Objects, Attributes, and Methods(对象,属性,方法)
Ruby 是面向对象的语言
Ruby 是面向对象的语言,所有跟我们打交道的在虚拟机里都是对象。每一片数据都是对象。对象拥有的信息,称之为属性。对象可以执行的动作,称之为方法。
举个关于对象的例子,想象一下你作为一个人。你的属性就是身高,体重,眼睛的颜色。你拥有的方法比如走路,跑步,洗碗,做梦。不同的对象有不同的属性和方法。下一个部分我们具体的看看 Ruby 中常见的对象。
类和实例
在面向对象语言中定义了类,类是同一类别或者同一类型的描述。它定义了这一类对象都有的属性和方法。
定义一个类
我们来想一下学校的模型。我们创建了一个 Student 类,代表所有的学生。学生类的属性有 first_name, last_name, primary_phone_number。还可以定义一个 introduction 方法,让学生自我介绍。
|
|
你可能还不了解 attr_accessor 方法,这是用来定义一个类的实例的属性。
创建实例
类本身不能表示一个学生,他只是学生的抽象表达。为了表示一个真实的学生,我们在类的基础上创建了实例。
想象一下你是一个学生。不是一个抽象的概念,你是一个真实存在的人。这个真实的人就是一个学生实例。他是一个抽象概念的实现。所有的属性都是真实的数据。first_name,last_name,primary_phone_number。
从文件里运行 Ruby
我们很少在 IRB 里定义类。它只是我们的草稿,随时验证我们的想法。我们来看看如果运行一个 Ruby 文件。
- 退出 IRB 会话
- 在命令行里建个文件夹,作为工作目录
- 用文本编辑器创建一个文件,命名为 student.rb
- 在编辑器里保存好
- 从命令行里运行
ruby student.rb 因为文件是空的,所以看不到输出。
创建一个学生类
在你的文本编辑器里,像这样的结构写一个类
|
|
在类里面定义方法的时候通常用 def 关键字:
|
|
我们注意到 puts 那一行是一个返回学生第一个名字的方法。我们添加上之前用到的属性。
|
|
运行这个文件
回到终端里,试着运行这个程序,还是没有任何输出…
为什么?我们定义了一个学生类,然后给这个类赋予了一个方法,还添加了一些属性。-但是我没有实际上没有创建任何的实例或者调用任何方法。
创建实例
我们定义了类之后,还要创建一个实例:frank = Student.new
我们调用了学生类的 new 方法。并且存储到一个叫做 frank 的变量里。一旦我们创建了实例,我们就能设置或者访问他们的属性,调用他们的方法。
在文件里创建实例
在 student.rb 下面,最后一个 end 后面,添加下面的字段。
|
|
保存之后,返回终端,再次运行 student.rb ,这时候应该输出 Hi,I'm Frank!
方法的参数
有时候方法会带有一个或者多个参数,告诉他们想做的要怎么做。比如,我想让 frank.introduction('katrina') 让他介绍自己给 Katrina .这个参数可以是 numbers,strings,或者任何对象。修改你的方法,让他携带一个参数:
|
|
现在重新运行文件,你就会看到 Hi Katrina, I'm Frank.
返回值
在 Ruby 里,每次调用方法都会有一个返回值。默认,Ruby 的方法会返回执行最后一个表达式的值。
添加 favorite_number
为我们的类添加一个 favorite_number 参数。
|
|
从你的终端里运行它,然后就会看到 Frank's favorite number is 7.最后这一行调用的就是 favorite_number 方法。最后那个方法的最后一行是 7,那么 7 就成了这个方法的返回值,返回给调用这个方法的人。在我们这里,返回了 7 并且作为插值,进入了字符串里。
现在你差不多入门了
好了,简单介绍到这儿了,现在你可以深入的探索诱人的 Ruby 了。
本文翻译至 Ruby in 100 Minutes
