一小时体验Ruby
所谓的一小时入门某一门语言都是有前提的,前提是你要有一门编程语言的基础才行,否则一个小时对你还是无济于事。
要使用Ruby
,首先要安装之。在Mac
电脑上默认就有安装,如果使用Ubuntu
,好像也是默认安装的。如果你使用RHEL系列,请自行安装。
对Ruby
的感受:Ruby
的设计哲学来源于Lisp
及Perl
。其类、方法定义(begin
开始、end
结尾)上很像Lisp
(左小括号开始、右小括号结束);变量命名上很像Perl
(我估计你要很长一段时间才能适应其变量命名方式,这种方式也本无好坏,习惯就好了)。
[toc]
查看一下系统上的Ruby版本:
LavenLius-MacPro:ruby liuchuan$ ruby --versionruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin16]
本文使用的是2.x
版本,2.x
版本是兼容1.9.x
版本的,但1.9.x
是不兼容1.8
版本的,请大家注意一下。
打印
打印Ruby的版本号
# -*- coding: utf-8 -*-# filename: test.rbp RUBY_VERSION# 或者在交互模式下LavenLius-MacPro:ruby liuchuan$ irbirb(main):002:0> p RUBY_VERSION"2.0.0"=> "2.0.0"
一个小例子:
# -*- coding: utf-8 -*-# filename: print01.rba = 5b = "rabbit"p a, b, 3 # prints each on a new line.=beginthis is a multi-line commentp a, b, 3can be written asp(a, b, 3)parenthesis are often not necessary=end
我们执行一下上述脚本:
LavenLius-MacPro:ruby liuchuan$ ruby print01.rb5"rabbit"3
puts
,print
与p
的区别:
- puts 以人类可读的形式打印,每个参数后面都加入了换行符。例如,字符以不带引号的形式打印,数组以不带中括号的形式打印。
- print 与puts很想,但不会在参数后面添加换行符。
- p 与puts相反,打印字符带引号等,对调试很有帮助。
换行与缩进在Ruby中的意义:
- 换行或分号用来分隔表达式。p(3);与p(3)在一行上是一样的,这点与Python类似;
- 缩进(tabs或空格)没有意义;Python中的缩进可以认为是语法规则;
- 函数或方法的参数是可选的(与Perl类似)。如p(3)与p 3相同;
字符串
单引号
单引号中的字符代表的字符的字面意义(literal string),例如\n
在单引号内就变得没有特殊意义了。
irb(main):010:0> aa = 'cat'=> "cat"irb(main):011:0> p aa"cat"=> "cat"irb(main):012:0> bb = '4\n5'=> "4\\n5"irb(main):013:0> p bb"4\\n5"=> "4\\n5"irb(main):014:0> cc = '1irb(main):015:0' 2'=> "1\n2"irb(main):016:0> p cc"1\n2"=> "1\n2"
双引号
当双引号中有变量或转义字符时,打印时将会求值。
irb(main):019:0> aa = 4=> 4irb(main):020:0> bb = "there are ${aa} cats"=> "there are ${aa} cats"irb(main):021:0> p bb"there are ${aa} cats"=> "there are ${aa} cats"irb(main):022:0> bb = "there are #{aa} cats" # 要想获得aa的值,使用#{aa}的形式获取=> "there are 4 cats"irb(main):023:0> p bb"there are 4 cats"=> "there are 4 cats"irb(main):025:0> p "there are #{1+2} cats""there are 3 cats"=> "there are 3 cats"
字符串长度
字符串有一个length
的方法,通过它我们可以获得字符串的长度。
irb(main):026:0> "hello world".length=> 11
有些语言是提供了len
方法来获取字符串的长度,Ruby这一点做的倒是挺方便。
子串
我们可以通过索引或下标获得字符串的某一个字符,或通过一个区间获得字符串指定范围内的子字符串。
irb(main):029:0> p "abcdefghijk"[2]"c"=> "c"irb(main):034:0> p "abcdefg"[2, 3] # 从第2个字符开始,打印3个"cde"=> "cde"
是不是很有意思,它居然可以这么玩:"abcdefg"[2, 3]
。
字符串操作
Ruby所提供的字符串操作功能还是比较方便的。
字符串连接
我们可以想其他语言一样,使用+
就可以连接两个或多个字符串了。太简单了,不愿意演示。
子串替换
既然字串可以被替换,那么说明Ruby中的字符串是可以被修改的。在很多语言中,字符串是不能被修改的。验证以下:
irb(main):001:0> a = "abcdefg"=> "abcdefg"irb(main):002:0> a[0]=> "a"irb(main):003:0> a[0] = "b"=> "b"irb(main):004:0> a=> "bbcdefg"
还可以通过切片(字串)的形式批量修改:
irb(main):005:0> xx = "0123456789"=> "0123456789"irb(main):006:0> xx[1]= "a"=> "a"irb(main):007:0> p xx"0a23456789"=> "0a23456789"irb(main):008:0> xx[1,8] = "what"=> "what"irb(main):009:0> p xx"0what9"=> "0what9"
字符串匹配
# get the start index of a substringp "in love?".index("love") # 3p "in love?".index("456") # nil (not found)
还可以分割字符串:
p "lavenliu is handsome".split(" ") # ["lavenliu", "is", "handsome"]
数字与字符串转换
通过对象的相应方法转换即可:
# string to intp "3".to_i # 3# string to floatp "3".to_f # 3.0# int to stringp 3.to_s # "3"# int to floatp 3.to_f # 3.0# float to stringp 3.0.to_s # "3.0"# float to intp 3.0.to_i # 3
需要注意的是,数字3.
必须以0结尾,否则.
就会被认为是方法调用。
一切皆对象-找到对象的方法
一切皆对象,这在动态的脚本语言中是很常见的现象。在Ruby中也是一样的,不过它体现的更加淋漓尽致。数字与字符串也是对象。既然是对象,那么就可以通过.name
(name
是对象具有的某种方法)来调用对象的方法。比如数字3,在Ruby中它就是一个对象,这个对象有to_i
方法,我们就可以3.to_i
进行调用。
要想找到一个对象具有哪些方法,可以通过其methods
方法来查看。如:
irb(main):012:0> p 3.methods[:-@, :**, :<=>, :upto, :<<, :<=, :>=, :==, :chr, :===, :>>, :[], :%, :&, :inspect, :+, :ord, :-, :/, :*, :size, :succ, :<, :>, :to_int, :coerce, :divmod, :to_s, :to_i, :fdiv, :modulo, :remainder, :abs, :magnitude, :gcd, :integer?, :gcdlcm, :numerator, :lcm, :to_r, :floor, :ceil, :round, :truncate, :rationalize, :to_f, :^, :odd?, :even?, :allbits?, :anybits?, :nobits?, :downto, :times, :pred, :pow, :bit_length, :digits, :denominator, :next, :div, :|, :~, :+@, :eql?, :singleton_method_added, :i, :real?, :zero?, :nonzero?, :finite?, :infinite?, :step, :positive?, :negative?, :real, :imaginary, :to_c, :angle, :phase, :imag, :abs2, :arg, :conjugate, :conj, :rectangular, :rect, :polar, :clone, :dup, :quo, :between?, :clamp, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :public_methods, :instance_variables, :method, :public_method, :define_singleton_method, :public_send, :singleton_method, :extend, :pp, :to_enum, :enum_for, :=~, :!~, :respond_to?, :freeze, :object_id, :send, :display, :nil?, :hash, :class, :singleton_class, :itself, :taint, :yield_self, :untaint, :tainted?, :untrusted?, :untrust, :frozen?, :trust, :methods, :singleton_methods, :protected_methods, :private_methods, :!, :equal?, :instance_eval, :instance_exec, :!=, :__id__, :__send__]
嚓!竟然有一大坨方法输出。我们注意到有的方法是以!
结尾的,它表示将要改变变量的值(也就是所谓的原地改变);有的方法是以?
结尾的,它将返回true
或false
(也就是所谓的断言函数)。
变量
在Ruby中,变量不需要声明(废话,在其他脚本语言中也特么是这样好吗?)。我们都知道在动态脚本语言中,变量是没有类型的,而其指向的值是有类型的。所有的动态语言基本都是这个德行。
找到值的类型:
p 5.kind_of?(Integer) # true. The "kind_of?" is a method.p 5.class # Fixnump "5".class # Stringp [3,4,5].class # Arrayp true.class # TrueClassp false.class # FalseClassp nil.class # NilClass
变量名前缀及其作用域
Ruby有4种变量作用域:{local ,global ,instance ,class }。变量名称的第一个字符决定它们的作用域: | 变量开始的字符 | 说明 |
---|---|---|
a...z 或者 _ | 小写字母或下划线,局部变量 | |
A...Z | 大写字母,常量 | |
$ | 全局变量 | |
@ | 实例变量,类 | |
@@ | 类变量,类 |
演示如下:
irb(main):035:0> a = 3=> 3irb(main):036:0> $a = 4=> 4irb(main):037:0> @a = 5=> 5irb(main):038:0> @@a = 6(irb):38: warning: class variable access from toplevel=> 6irb(main):039:0> p defined?(a)"local-variable"=> "local-variable"irb(main):040:0> p defined?($a)"global-variable"=> "global-variable"irb(main):041:0> p defined?(@a)"instance-variable"=> "instance-variable"irb(main):042:0> p defined?(@@a)(irb):42: warning: class variable access from toplevel"class variable"=> "class variable"
Ruby中的常量是强制以大写字母开头的,我们可以再次给常量赋值,解释器会给我们警告,但不会抛出异常。这在其他语言中是不被允许的。通过上述演示,其实Ruby还是挺灵活的,不限制你做任何事,甚至是违反常规的事。这主要是继承了Perl的灵活性。灵活是一把双刃剑,关键是你怎么看。
预定义的全局变量
Variable Name | Variable Value |
---|---|
$0 | 当前执行脚本的名称 |
$* | 命令行参数(一个数组) |
$$ | Ruby进程ID |
$? | 最后一个子进程的退出状态 |
还有很多预定义的全局变量,可以自行查找。
看个简单的例子吧:
print "the script name is: ", $0, "\n"print "command line are: ", $*, "\n"print "process id is: ", $$, "\n"print "the last child process exit status is: ", $!, "\n"
执行结果为:
$ ruby test02.rb x y zthe script name is: test02.rbcommand line are: ["x", "y", "z"]process id is: 2996the last child process exit status is:
真与假
true
与false
是内建的对象。下面两种情况被认为是false:
false
nil
其他情况则视为true
(包括0值,空字符串,空数组等也属于true
)。跟Lua
一模一样。
控制条件
这一块比较简单,直接就上演示代码了。
if
简单的if-then
语句:
xx = 4if xx > 0 then p 1 end # prints 1# formatted in multiple linesif xx > 0 then p 1end # prints 1
if-then-else
语句:
xx = 4if xx > 0 then p 1 else p 0 end # prints 1
多个else
:
xx = 4# multiple "else if"if xx > 4 then p 1elsif xx < 4 then p 0elsif xx == 4 then p "yy"end # prints "yy"# formatted in another wayif xx > 4 then p 1elsif xx < 4 then p 0elsif xx == 4 then p "yy"end # prints "yy"# formatted in one lineif xx > 4 then p 1 elsif xx < 4 then p 0 elsif xx == 4 then p "yy" end # prints "yy"
Ruby的if
很像Lisp的let
,它可以返回最后一个表达式的值:
xx = 4p(if xx > 0 then 1 end) # prints 1p(if xx > 0 then 1 else 0 end) # prints 1p(if xx > 4 then 1elsif xx < 4 then 0elsif xx == 4 then "yes"end) # prints "yes"# short formp( xx > 2 ? 1 : 0) # prints 1
你可以认为任何东东都是一个表达式,并且它们都可以返回一个值。
我们再看看case
语句,类似其他语言当中的switch
,及Lisp中的cond
。一个例子:
xx = 3myResult =case xx when 1 then "one" when 2 then "two" when 3 then "three" when 3 then "four" else "cat"endp myResult # "three"
for
for
很像Bash
中的for
。一个例子:
for ii in 0..4 do p ii end # prints 0 to 4
嵌套for
循环:
for ii in 1..2 do for jj in 1..3 do puts "#{ii}, #{jj}" endend# prints# 1, 1# 1, 2# 1, 3# 2, 1# 2, 2# 2, 3# can also be written in one line.for ii in 1..2 do for jj in 1..3 do puts "#{ii}, #{jj}" end end
while
直接看例子:
ii = 1while ii < 9 doputs ii;if ii == 5 then break endii += 1end# prints 1 to 5
我们知道在Python
中通过range
来创建一个范围(开闭区间)。在Ruby
中,我们通过(from..to).to_a
的形式来创建一个范围(开闭区间)。如:
a = (1..5).to_a # "to_a" converts to arrayp a # [1, 2, 3, 4, 5]# 更多例子p (1..5).class # Range# methods for the rangep (1..5).methods # [:==, :===, :eql?, :hash, :each, :step, :begin, :end, :first, :last, :min, :max, :to_s, :inspect, :exclude_end?, :member?, :include?, :cover?, :to_a, :entries, :sort, :sort_by, :grep, :count, :find, :detect, :find_index, :find_all, :select, :reject, :collect, :map, :flat_map, :collect_concat, :inject, :reduce, :partition, :group_by, :all?, :any?, :one?, :none?, :minmax, :min_by, :max_by, :minmax_by, :each_with_index, :reverse_each, :each_entry, :each_slice, :each_cons, :each_with_object, :zip, :take, :take_while, :drop, :drop_while, :cycle, :chunk, :slice_before, :nil?, :=~, :!~, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]
数据结构
这里简单介绍一下数组及哈希表。
列表/数组
创建一个数组:
# creating a arrayaa = [3, "four",5]p aa # prints [3, "four", 5]# another waybb = Array.new(3)p bb # prints [nil, nil, nil]# create array of 3 elements, with value of 1cc = Array.new(3,1)p cc # prints [1, 1, 1]# array can be nestedaa = [0, 1, 2, ["8", 9], 3]
计算数组的长度:
aa = [3, "four",5]p aa.length # 3
我们看到,通过array_name.length
的方式就可以获取数组的长度。在其他语言中,我们要通过len
这样的方法来获取数组的长度。在Ruby
中,数组的长度只是它的一个属性而已,可见这门语言的自省能力是非常强的。
要获取一个元素,跟其他语言并无区别,通过下标或索引来获取元素。
数组的其他特性这里不做介绍了,非常简单,与Python
非常类似。
哈希表
哈希表也就是其他语言当中的字典或关联数组。
直接看个例子:
# define a keyed listhh = {:john => 3, :mary => 4, :jane => 5, :vicky => 7}p hh # {:john=>3, :mary=>4, :jane=>5, :vicky=>7}# getting value from a keyp hh[:mary] # 4# add a entryhh[:pretty] = 99p hh # {:john=>3, :mary=>4, :jane=>5, :vicky=>7, :pretty=>99}# delete a entryhh.delete :vickyp hh # {:john=>3, :mary=>4, :jane=>5, :pretty=>99}# get all keysp hh.keys # [:john, :mary, :jane, :pretty]# get all valuesp hh.values # [3, 4, 5, 99]# check if a key existsp hh.has_key? :mary # truep hh.has_value? :jenny # false
在Ruby
中,:something
是一个符号(Symbol),类似Lisp
中的Symbol。我们可以认为Symbol是一个静态的字符串。
当然,我们也可以这些定义一个哈希表:
# hash, with keys as stringaa = {'john' => 3, 'mary' => 4, 'jane' => 5}# hash, with keys as symbol.bb = {:john => 3, :mary => 4, :jane => 5}
一个问题:Symbol作为哈希表的key与字符串作为哈希表的key有什么差别吗?
调用Unix命令
调用系统命令很简单,使用反引号把要执行的命令包括一下就可以了。与Perl
一模一样。一个例子:
irb(main):008:0> puts `ls -l ~`total 0drwx------ 5 liuchuan staff 170 Aug 5 2016 Applicationsdrwx------+ 13 liuchuan staff 442 Sep 13 17:25 Desktopdrwx------+ 30 liuchuan staff 1020 Aug 18 12:24 Documentsdrwx------+ 233 liuchuan staff 7922 Sep 13 19:13 Downloadsdrwxr-xr-x 6 liuchuan staff 204 Jan 11 2017 HBuilderdrwxr-xr-x 8 liuchuan staff 272 Apr 2 16:24 HBuilderProjectsdrwxr-xr-x 10 liuchuan staff 340 Jul 22 10:53 IdeaProjectsdrwx------@ 82 liuchuan staff 2788 Dec 21 2017 Librarydrwxr-xr-x 4 liuchuan staff 136 Aug 18 21:47 MarkEditorQuickNotedrwx------+ 13 liuchuan staff 442 Jul 5 10:03 Moviesdrwx------+ 67 liuchuan staff 2278 Nov 1 2017 Musicdrwx------+ 31 liuchuan staff 1054 Aug 11 15:56 Picturesdrwxr-x---+ 6 liuchuan staff 204 Jul 14 2016 Publicdrwxr-xr-x 5 liuchuan staff 170 Sep 10 13:53 PycharmProjectsdrwxr-xr-x 8 liuchuan staff 272 Aug 12 17:42 VirtualBox VMsdrwxr-xr-x 7 liuchuan staff 238 Sep 6 15:04 godrwxr-xr-x 17 liuchuan staff 578 May 5 12:30 jython2.7.0drwx------ 18 liuchuan staff 612 Sep 13 19:27 nginxdrwxr-xr-x 55 liuchuan staff 1870 Sep 7 11:01 programmingdrwxr-xr-x 11 liuchuan staff 374 Aug 9 13:47 quicklispdrwxr-xr-x@ 47 liuchuan staff 1598 Sep 10 15:35 softwares=> nil
定义函数
使用def
关键字来定义函数,由end
结束定义。一个简单的例子:
irb(main):001:0> def helloirb(main):002:1> "hello, python"irb(main):003:1> end=> :helloirb(main):004:0> hello=> "hello, python"irb(main):005:0> a = hello=> "hello, python"irb(main):006:0> a=> "hello, python"
看到了上述的一个简单的函数中,我们并没有使用return
关键字。如果一个函数中没有使用return
关键字,那么该函数的返回值就是最后一个表达式的值。
定义一个带参数的函数:
def f(x) x+1 endputs f(4) # prints 5
定义一个带默认参数的:
# function with default valuedef f(x = 3) x+1 endputs f # prints 4
定义一个可变参数的函数:
irb(main):016:0> def ff(*params)irb(main):017:1> params.each {|string| puts string}irb(main):018:1> end=> :ffirb(main):019:0> ff(3, 4, 5)345=> [3, 4, 5]
是不是跟Python
很像?参数解构。
关于函数的内容还有很多,不过形式上与其他脚本语言很像。
类与对象
定义一个类的话,需要以大写字母开头。
- 实例变量 每个实例都拥有不同的拷贝。改变一个实例的变量,并不会影响其他实例中的实例变量。实例变量以
@
开头来定义。 - 类变量 每个实例拥有共享一份类变量。类变量以
@@
开头来定义。
一个简单的示例:
# filename: small_ruby.rbclass SmallRuby # initializer def initialize(ii) @xx = ii # @xx is a instance variable end # a method. Return the instance variable @xx def mm @xx end # a another method. def nn(aa) @xx + aa endend# create a object.myobj = SmallRuby.new(3)# call a methodp myobj.mm # 3# call another method with argumentp myobj.nn(2) # 5
执行上述脚本:
$ ruby small_ruby.rb35
如果我们要访问实例变量xx
怎么办呢?可以直接通过myobj.xx
来访问吗?
关于类还有很多内容,诸如继承相关的内容请自行解决。
模块
有了前面介绍的基础,我们就可以写个小模块了。模块必须是大写字母开头。
一个例子:
# filename: mymodule.rbmodule MyModule NAME = "Ruby" def MyModule.my_method "hello, my module" endend
使用上面定义的模块:
require "./mymodule"# 通过类方法,我们可以在类方法名称前面放置模块名称和一个点号来调用模块方法,# 还可以使用模块名称和两个冒号来引用一个常量。p MyModule::NAMEp MyModule.my_method
我们可以在类方法名称前面放置模块名称和一个点号来调用模块方法,还可以使用模块名称和两个冒号来引用一个常量。(跟C++
中的::
很像。这真是天下语言一大抄,天下文章也是一大抄。)
执行上述代码:
$ ruby use_my_module.rb"Ruby""hello, my module"
总结
好了,就这么多吧。小白也是看着官方文档自学的,内容太多了,只是挑选了一些比较基础的东东来分享。英语是到坎儿,如果看不大懂官方文档,自学起来就比较慢;或者去看别人翻译的文档(我觉得还是自己去摸索来找到知识的源头为好)。
如果你觉得自己入门了Ruby
,那么推荐试试Rails
这个Web框架。听说开发效率挺高的。