一小时体验Ruby

所谓的一小时入门某一门语言都是有前提的,前提是你要有一门编程语言的基础才行,否则一个小时对你还是无济于事。

创新互联专业为企业提供西和网站建设、西和做网站、西和网站设计、西和网站制作等企业网站建设、网页设计与制作、西和企业网站模板建站服务,十多年西和做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。

要使用Ruby,首先要安装之。在Mac电脑上默认就有安装,如果使用Ubuntu,好像也是默认安装的。如果你使用RHEL系列,请自行安装。

Ruby的感受:Ruby的设计哲学来源于LispPerl。其类、方法定义(begin开始、end结尾)上很像Lisp(左小括号开始、右小括号结束);变量命名上很像Perl(我估计你要很长一段时间才能适应其变量命名方式,这种方式也本无好坏,习惯就好了)。

[toc]

查看一下系统上的Ruby版本:

LavenLius-MacPro:ruby liuchuan$ ruby --version
ruby 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.rb

p RUBY_VERSION

# 或者在交互模式下
LavenLius-MacPro:ruby liuchuan$ irb
irb(main):002:0> p RUBY_VERSION

"2.0.0"

=> "2.0.0"

一个小例子:

# -*- coding: utf-8 -*-
# filename: print01.rb
a = 5
b = "rabbit"
p a, b, 3               # prints each on a new line.

=begin
this is a multi-line comment

p a, b, 3

can be written as

p(a, b, 3)

parenthesis are often not necessary
=end

我们执行一下上述脚本:

LavenLius-MacPro:ruby liuchuan$ ruby print01.rb
5
"rabbit"
3

putsprintp的区别:

  • 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 = '1

irb(main):015:0' 2'

=> "1\n2"

irb(main):016:0> p cc

"1\n2"

=> "1\n2"

双引号

当双引号中有变量或转义字符时,打印时将会求值。

irb(main):019:0> aa = 4

=> 4

irb(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 substring
p "in love?".index("love") # 3
p "in love?".index("456") # nil (not found)

还可以分割字符串:

p "lavenliu is handsome".split(" ") # ["lavenliu", "is", "handsome"]

数字与字符串转换

通过对象的相应方法转换即可:

# string to int
p "3".to_i # 3

# string to float
p "3".to_f # 3.0

# int to string
p 3.to_s # "3"

# int to float
p 3.to_f # 3.0

# float to string
p 3.0.to_s # "3.0"

# float to int
p 3.0.to_i # 3

需要注意的是,数字3.必须以0结尾,否则.就会被认为是方法调用。

一切皆对象-找到对象的方法

一切皆对象,这在动态的脚本语言中是很常见的现象。在Ruby中也是一样的,不过它体现的更加淋漓尽致。数字与字符串也是对象。既然是对象,那么就可以通过.namename是对象具有的某种方法)来调用对象的方法。比如数字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__]

嚓!竟然有一大坨方法输出。我们注意到有的方法是以!结尾的,它表示将要改变变量的值(也就是所谓的原地改变);有的方法是以?结尾的,它将返回truefalse(也就是所谓的断言函数)。

变量

在Ruby中,变量不需要声明(废话,在其他脚本语言中也特么是这样好吗?)。我们都知道在动态脚本语言中,变量是没有类型的,而其指向的值是有类型的。所有的动态语言基本都是这个德行。

找到值的类型:

p 5.kind_of?(Integer) # true. The “kind_of?” is a method.

p 5.class # Fixnum
p "5".class # String
p [3,4,5].class # Array
p true.class # TrueClass
p false.class # FalseClass
p nil.class # NilClass

变量名前缀及其作用域

Ruby有4种变量作用域:{localglobalinstanceclass}。变量名称的第一个字符决定它们的作用域:
变量开始的字符说明
a...z 或者 _ 小写字母或下划线,局部变量
A...Z 大写字母,常量
$ 全局变量
@ 实例变量,类
@@ 类变量,类

演示如下:

irb(main):035:0> a = 3

=> 3

irb(main):036:0> $a = 4

=> 4

irb(main):037:0> @a = 5

=> 5

irb(main):038:0> @@a = 6

(irb):38: warning: class variable access from toplevel

=> 6

irb(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 NameVariable 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 z
the script name is: test02.rb
command line are: ["x", "y", "z"]
process id is: 2996
the last child process exit status is: 

真与假

truefalse是内建的对象。下面两种情况被认为是false:

  • false
  • nil

其他情况则视为true(包括0值,空字符串,空数组等也属于true)。跟Lua一模一样。

控制条件

这一块比较简单,直接就上演示代码了。

if

简单的if-then语句:

xx = 4

if xx > 0 then p 1 end # prints 1

# formatted in multiple lines
if xx > 0 then
  p 1
end # prints 1

if-then-else语句:

xx = 4

if xx > 0 then p 1 else p 0 end # prints 1

多个else

xx = 4

# multiple “else if”
if xx > 4 then
  p 1
elsif xx < 4 then
  p 0
elsif xx == 4 then
  p "yy"
end # prints "yy"

# formatted in another way
if xx > 4 then p 1
elsif xx < 4 then p 0
elsif xx == 4 then p "yy"
end # prints "yy"

# formatted in one line
if 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 = 4

p(if xx > 0 then 1 end) # prints 1

p(if xx > 0 then 1 else 0 end) # prints 1

p(
if xx > 4 then 1
elsif xx < 4 then 0
elsif xx == 4 then "yes"
end
) # prints "yes"

# short form
p( xx > 2 ? 1 : 0) # prints 1

你可以认为任何东东都是一个表达式,并且它们都可以返回一个值。

我们再看看case语句,类似其他语言当中的switch,及Lisp中的cond。一个例子:

xx = 3

myResult =
case xx
  when 1 then "one"
  when 2 then "two"
  when 3 then "three"
  when 3 then "four"
  else "cat"
end

p 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}"
  end
end

# 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 = 1

while ii < 9 do
puts ii;
if ii == 5 then break end
ii += 1
end

# prints 1 to 5

我们知道在Python中通过range来创建一个范围(开闭区间)。在Ruby中,我们通过(from..to).to_a的形式来创建一个范围(开闭区间)。如:

a = (1..5).to_a # “to_a” converts to array
p a # [1, 2, 3, 4, 5]

# 更多例子
p (1..5).class # Range

# methods for the range
p (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 array
aa = [3, "four",5]
p aa # prints [3, "four", 5]

# another way
bb = Array.new(3)
p bb # prints [nil, nil, nil]

# create array of 3 elements, with value of 1
cc = Array.new(3,1)
p cc # prints [1, 1, 1]

# array can be nested
aa = [0, 1, 2, ["8", 9], 3]

计算数组的长度:

aa = [3, "four",5]
p aa.length # 3

我们看到,通过array_name.length的方式就可以获取数组的长度。在其他语言中,我们要通过len这样的方法来获取数组的长度。在Ruby中,数组的长度只是它的一个属性而已,可见这门语言的自省能力是非常强的。

要获取一个元素,跟其他语言并无区别,通过下标或索引来获取元素。

数组的其他特性这里不做介绍了,非常简单,与Python非常类似。

哈希表

哈希表也就是其他语言当中的字典或关联数组。

直接看个例子:

# define a keyed list
hh = {:john => 3, :mary => 4, :jane => 5, :vicky => 7}
p hh # {:john=>3, :mary=>4, :jane=>5, :vicky=>7}

# getting value from a key
p hh[:mary] # 4

# add a entry
hh[:pretty] = 99
p hh # {:john=>3, :mary=>4, :jane=>5, :vicky=>7, :pretty=>99}

# delete a entry
hh.delete :vicky
p hh # {:john=>3, :mary=>4, :jane=>5, :pretty=>99}

# get all keys
p hh.keys # [:john, :mary, :jane, :pretty]

# get all values
p hh.values # [3, 4, 5, 99]

# check if a key exists
p hh.has_key? :mary # true

p hh.has_value? :jenny # false

Ruby中,:something是一个符号(Symbol),类似Lisp中的Symbol。我们可以认为Symbol是一个静态的字符串。

当然,我们也可以这些定义一个哈希表:

# hash, with keys as string
aa = {'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 0
drwx------ 5 liuchuan staff 170 Aug 5 2016 Applications
drwx------+ 13 liuchuan staff 442 Sep 13 17:25 Desktop
drwx------+ 30 liuchuan staff 1020 Aug 18 12:24 Documents
drwx------+ 233 liuchuan staff 7922 Sep 13 19:13 Downloads
drwxr-xr-x 6 liuchuan staff 204 Jan 11 2017 HBuilder
drwxr-xr-x 8 liuchuan staff 272 Apr 2 16:24 HBuilderProjects
drwxr-xr-x 10 liuchuan staff 340 Jul 22 10:53 IdeaProjects
drwx------@ 82 liuchuan staff 2788 Dec 21 2017 Library
drwxr-xr-x 4 liuchuan staff 136 Aug 18 21:47 MarkEditorQuickNote
drwx------+ 13 liuchuan staff 442 Jul 5 10:03 Movies
drwx------+ 67 liuchuan staff 2278 Nov 1 2017 Music
drwx------+ 31 liuchuan staff 1054 Aug 11 15:56 Pictures
drwxr-x---+ 6 liuchuan staff 204 Jul 14 2016 Public
drwxr-xr-x 5 liuchuan staff 170 Sep 10 13:53 PycharmProjects
drwxr-xr-x 8 liuchuan staff 272 Aug 12 17:42 VirtualBox VMs
drwxr-xr-x 7 liuchuan staff 238 Sep 6 15:04 go
drwxr-xr-x 17 liuchuan staff 578 May 5 12:30 jython2.7.0
drwx------ 18 liuchuan staff 612 Sep 13 19:27 nginx
drwxr-xr-x 55 liuchuan staff 1870 Sep 7 11:01 programming
drwxr-xr-x 11 liuchuan staff 374 Aug 9 13:47 quicklisp
drwxr-xr-x@ 47 liuchuan staff 1598 Sep 10 15:35 softwares
=> nil

定义函数

使用def关键字来定义函数,由end结束定义。一个简单的例子:

irb(main):001:0> def hello
irb(main):002:1> "hello, python"
irb(main):003:1> end
=> :hello
irb(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 end

puts f(4) # prints 5

定义一个带默认参数的:

# function with default value
def f(x = 3) x+1 end

puts f # prints 4

定义一个可变参数的函数:

irb(main):016:0> def ff(*params)
irb(main):017:1> params.each {|string| puts string}
irb(main):018:1> end
=> :ff
irb(main):019:0> ff(3, 4, 5)
3
4
5
=> [3, 4, 5]

是不是跟Python很像?参数解构。

关于函数的内容还有很多,不过形式上与其他脚本语言很像。

类与对象

定义一个类的话,需要以大写字母开头。

  • 实例变量每个实例都拥有不同的拷贝。改变一个实例的变量,并不会影响其他实例中的实例变量。实例变量以@开头来定义。
  • 类变量每个实例拥有共享一份类变量。类变量以@@开头来定义。

一个简单的示例:

# filename: small_ruby.rb
class 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
  end

end

# create a object.
myobj = SmallRuby.new(3)

# call a method
p myobj.mm # 3

# call another method with argument
p myobj.nn(2) # 5

执行上述脚本:

$ ruby small_ruby.rb
3
5

如果我们要访问实例变量xx怎么办呢?可以直接通过myobj.xx来访问吗?

关于类还有很多内容,诸如继承相关的内容请自行解决。

模块

有了前面介绍的基础,我们就可以写个小模块了。模块必须是大写字母开头。

一个例子:

# filename: mymodule.rb
module MyModule
  NAME = "Ruby"
  def MyModule.my_method
    "hello, my module"
  end
end

使用上面定义的模块:

require "./mymodule"

# 通过类方法,我们可以在类方法名称前面放置模块名称和一个点号来调用模块方法,
# 还可以使用模块名称和两个冒号来引用一个常量。
p MyModule::NAME
p MyModule.my_method

我们可以在类方法名称前面放置模块名称和一个点号来调用模块方法,还可以使用模块名称和两个冒号来引用一个常量。(跟C++中的::很像。这真是天下语言一大抄,天下文章也是一大抄。)

执行上述代码:

$ ruby use_my_module.rb
"Ruby"
"hello, my module"

总结

好了,就这么多吧。小白也是看着官方文档自学的,内容太多了,只是挑选了一些比较基础的东东来分享。英语是到坎儿,如果看不大懂官方文档,自学起来就比较慢;或者去看别人翻译的文档(我觉得还是自己去摸索来找到知识的源头为好)。

如果你觉得自己入门了Ruby,那么推荐试试Rails这个Web框架。听说开发效率挺高的。


网页题目:一小时体验Ruby
本文路径:http://pwwzsj.com/article/gpsdih.html