Ruby Programming/Syntax/Literals

维基教科书,自由的教学读本
上一项: 变数与常数 索引 下一项: 运算子

数值(Numeric)[编辑]

123                       # 整數
-123                      # 有號整號
1_123                     # 整數 (忽略底線)
-543                      # 負整數
123_456_789_123_456_789   # 長整數
123.45                    # 浮點數
1.2e-3                    # 浮點數 (科學記號式)
0xaabb                    # 十六進位整數
0377                      # 八進位整數
-0b1010                   # 二進位補數
0b001_001                 # 二進位整數
?a                        # ASCII 字元 'a' (97) 
?\C-a                     # Control-a (1)
?\M-a                     # Meta-a (225);這是 Macintosh 鍵盤按鍵。
?\M-\C-a                  # Meta-Control-a (129);這是 Macintosh 鍵盤按鍵。

字串(String)[编辑]

Ruby 可以像数值资料般妥善处理字串。字串是一个被双引号 ("...") 或单引号 ('...') 括起的内容。除此之外, Ruby 也可以 %q%Q 自定括号符号。

双引号单引号在某些案例上具有不同的效果。由双引号括起的字串允许以反斜线记号 (添加一个引导的反斜线 (\) 在字元前) 转换该字元之意义 - 通称转义(escaping),也可以窜写内容 (以 #{} 嵌入一段可估算的式子)。由单引号括起的字串不做这些解释工作,除了 \' 和 \\;你将得到你所看到的。例如:

irb> puts "a\nb\nc"
a
b
c
=> nil
irb> puts 'a\nb\n'
a\nb\nc
=> nil
irb> "\n"
=> "\n"
irb> '\n'
=> "\\n"
irb> "\001"
=> "\001"
irb> '\001'
=> "\\001"
irb> "abcd #{5*3} efg"
=> "abcd 15 efg"
irb> var = " abc "
=> " abc "
irb> "1234#{var}5678"
=> "1234 abc 5678"

单引号括号[编辑]

单引号只允许两个转义字元

  • \' – 单引号
  • \\ – 单斜线

双引号括号[编辑]

双引号括起的字串允许以反斜线记号 (添加一个引导的反斜线 (\) 在字元前) 转换该字元之意义,也可以窜写内容 (以 #{} 嵌入一段可估算的式子)。

反斜线记号(Backslash Notation)[编辑]

范例:

"this is a\ntwo line string"
"this is string with \"quotes\" word"
\n 换行 (0x0a) \s 空格 (0x20)
\r 归位(carriage return) (0x0d) \nnn 以八进制码 nnn 表示一个字元
\t 定位调整(tab) (0x09) \xnn 以十六进制码 nn 表示一个字元
\v 垂直定位(vertical tab) (0x0b) \cx control-x
\f 换页 (0x0c) \C-x control-x
\b 退格(backspace) (0x08) \M-x meta-x
\a 向音/警示音 (0x07) \M-\C-x meta-control-x
\e Escape字元 (0x1b) \x 表示字元x 本身,即不转义,例如: \"

窜写(Interpolation)[编辑]

#{expression} 语法在字串中嵌入一段式子,并以式子之估算值窜写式子内容。记得,单引号括起的字串不理会窜写。范例:

 "1 + 2 = #{1 + 2}"    #=> "1 + 2 = 3"
 '1 + 2 = #{1 + 2}'    #=> "1 + 2 = #{1 + 2}"

式子 1+2 之计算结果为 3 ,以此值窜写字串中之 #{ 1 + 2} ,故得 "1 + 2 = 3"。

几乎所有 Ruby 式子都可以被嵌入。若被嵌入的式子中使用了不存在的变数 (尚未指派内容) ,则会引发 NameError 异常:

 "trying to print #{undefined} variable"
 NameError: undefined local variable or method `undefined' for main:Object

指令窜写(Command Interpolation)[编辑]

指令窜写是另一种窜写作业,它使用反引号(`) - PC键盘上方数字键 1 左边的按键符号 - 括住一段命令列指令,令系统执行指令后再撷取指令之输出结果,最后以输出结果窜写指令内容并回传为一字串,完成指令窜写作业。由于指令内容必须为系统中存在之可执行程式,而可用之指令随系统有所差异,故此方式未必可得到结果。

指令窜写可以 %x 指定反引号(`)之替用符号。

范例 (Unix类系统):

d = `date`
d_parts = d.split(' ')
puts "Today is #{d_parts[0]}"
%x(uname -a)

范例 (Windows系统):

exec_path = `echo %PATH%`
paths = exec_path.split(';')
%x[echo %OS%]

Alternate Notation[编辑]

'%'记号

Ruby 正式语法使用单引号与双引号作为字串的括号。但 Ruby 从 Perl 语言中得到一个关于字串括号的古怪灵感:用 % (百分比字元) 指定字串的括号符号。例如: %[], %(), %{} 。除成对括弧符号外也可用单独字元,如 %!!, %@@ 。在一个 % 字元后得指定一个修饰字元,诸如: %q[], %Q[], %x ,它们决定是否套用反斜线记号窜写规则。详见单引号的替用者双引号的替用者

范例:

 '不套用竄寫與反斜線記號規則'
 "套用竄寫 #{interpolation}, 與反斜線記號 - \n"
 %q(不套用竄寫)
 %Q(套用竄寫 #{interpolation} 與反斜線記號 - \n)
 %(竄寫與反斜線)
 `echo "指令竄寫與反斜線"`
 %x(echo "指令竄寫與反斜線")

上述所有例子都有建置一个字串变数之效果。

当我们需要建置一个含有括号字元的字串却又不想使用反斜线记号为括号字元转义时,便经常运用'%'记号自行指定字串括号之符号。以自行指定的符号作为括号的替用者。

 "string \"with\" quotes"    #字串中含有雙引號("),必須以反斜線記號轉義
 %{string "with" quotes}     #以'%'記號指定大括弧{}為替用括號,便不需為雙引號轉義。
 %!another string example!         => "another string example"
 %@another "obnoxious" example@    => "another \"obnoxious\" example"

%r 可指定字样规则式之边界符号(/ 字元)的替用者。例如下列二式同义:

 response.gsub! /<.*?>/, ""    # 移除所有標籤
 response.gsub! %r{<.*?>}, ""  # 以 %r 指定大括弧為 / 符號的替用者。

%w%W可指定阵列之边界符号([ ])的替用者。还有一个附带效果:将其中所有字面值都视为字串;单、双引号将被视为字串的一部分而非字串的括号。

范例:

ar = ['123', 'abc', 'xyz']
=> ["123", "abc", "xyz"]
ar = %w< 123, abc, xyz >
=> ["123", "abc", "xyz"]
ar = %w! 123, abc, xyz !
=> ["123", "abc", "xyz"]

ar = ['I\'s']
=> ["I's"]
ar = %w[ I's ]
=> ["I's"]

%记号修饰字元与效果表[编辑]

% 指定字串括号的替用符号,并套用双引号括号规则。详见[[#双引号的替用者|双引号的替用
%Q 指定字串括号的替用符号,并套用双引号括号规则。详见双引号的替用者
%q 指定字串括号的替用符号,并套用单引号括号规则。详见单引号的替用者
%r 指定字样规则式之边界符号(/ 字元)的替用符号。
%s 指定符号之边界符号(:)的替用符号。
%w 指定阵列之边界符号([ ])的替用符号。
%W 指定阵列之边界符号([ ])的替用符号,并套用双引号括号规则。
%x 指定指令窜写之边界符号(`)的替用符号。

单引号的替用者[编辑]

举例而言,我们想用字串表达档案路径名称时,通常用单引号括起字串内容,如下所示:

puts 'c:\bus schedules\napolean\the portland bus schedule.txt'

用单引号括起字串内容时,不会转义其中的 \b, \n, 和 \t 字元,故得以直率地表达档案路径。但如果档案路径名称之中含有 ' 时,我们就要写成 \' ,如下所示:

puts 'c:\napolean\'s bus schedules\tomorrow\'s bus schedule.txt'

当字串中含有许多转义字元时,将会降低程式的可读性。所幸在 Ruby 中有更好的方式可用。你可以用 %q 自定括号,以 %q 自定之括号将套用单引号括号规则。你可以指定任何字元为括号。下列示范自定括号的用法,每一行都会印出同样的文字 - "c:\napolean's documents\tomorrow's bus schedule.txt" 。

puts %q!c:\napolean's documents\tomorrow's bus schedule.txt!
puts %q/c:\napolean's documents\tomorrow's bus schedule.txt/
puts %q^c:\napolean's documents\tomorrow's bus schedule.txt^
puts %q(c:\napolean's documents\tomorrow's bus schedule.txt)
puts %q{c:\napolean's documents\tomorrow's bus schedule.txt}
puts %q<c:\napolean's documents\tomorrow's bus schedule.txt>

若你选择的自定括号也同样被用于字串文字,你仍然需要转义。如下例以 # 为自定括号,但字串文字中也有一个 # 字元,此时就要写成 \#

 puts %q#c:\napolean's documents\tomorrow's \#9 bus schedule.txt#

当然我们用自定括号的意义在于避免这种情形,所以应尽量选用不会出现在字串文字中的字元做为自定括号。若你使用可成对的括弧字元为自定括号,你可用巢状括弧而不需转义。

puts %q(c:\napolean's documents\the (bus) schedule.txt)
puts %q{c:\napolean's documents\the {bus} schedule.txt}
puts %q<c:\napolean's documents\the <bus> schedule.txt>

双引号的替用者[编辑]

%Q 允许你自定套用双引号括号规则的括号符号,其用法如同 %q

print %Q^Say:\tHello world\n\tHello world\n^
print %Q(Say:\tHello world\n\tHello world\n)

你同样可以在其中嵌入 Ruby 的式子窜写字串内容。

name = 'Charlie Brown'

puts %Q!Say "Hello," #{name}.!
puts %Q/What is "4 plus 5"? Answer: #{4+5}/

字样规则式(Regular Expression)[编辑]

在 Unix 的传统中,我们用一种被称为字样规则式(regular expressions)的样式文法,表达字样在字串中出现的规则。附带一提,Regular expression 时常被译为常规表示法,词不达意。字样规则式以下列字元和字元组合的奇妙字汇撰写一个简明扼要的样式(pattern) ,用于测试一个字串是否与此字样相符。

[] 指定范围 (例如: [a-z] 表示一个在 a 到 z 范围内的字母)
\w 字母或十进制数之字元;与 [0-9A-Za-z] 相同
\W 非字母与十进制数之字元
\s 空白字元;与 [ \t\n\r\f] 相同
\S 非空白字元
\d 十进制数之字元;与 [0-9] 相同
\D 非十进制数之字元
\b 退格 (0x08) (若用于指定范围时)
\B 单字边界 (若不用于指定范围时)
* 零到数个其前一符号的内容
+ 一到数个其前一符号的内容
{m,n} 最少 m 个且最多 n 个其前一符号的内容
? 零到一个其前一符号的内容;与 {0,1} 相同
| 符合 '|' 之前一符号或后一符号的内容
() 分组。分组可以标示子样式(subpattern),并依出现顺序赋予可参照之变数名称,如 $1, $2 。若不要赋予分组可参照之变数名称,则应于开头加上?:指示不予编号,例如 /(?:\w+)/ 。

Ruby 和 Perl 一样用斜线 (/) 包围样式内容而不是用引号。若你以前从未用过字样规则式,它们或许看来散漫无章,但其中实有规律性存在。你应明智地花一些时间熟悉它们。它们具有强大及有效率的表达能力。当你需要进行样式比对、搜寻、或其他文字之操纵时,它们将省去许多令人头痛的事 (和许多行程式码)。

举个例子,假设我们想要测试一个字串是否符合此一描述:“以小写 f 开始,紧跟着一个大写字母;之后可能有一些废话,只要其中没有任何小写字母。” 若你是个老练的 C 程序员,你脑海中或许已经写下几十行程式码了。在 Ruby 中,你只需要用字样规则 /^f[A-Z][^a-z]*$/ 就可以测试你的字串了。

那么“包含以角括号圈住的十六进制数字?”又如何?

irb> def chab(s)   # "contains hex in angle brackets"
irb>     (s =~ /<0(x|x)(\d|[a-f]|[a-f])+>/) != nil
irb> end
=> nil
irb> chab "Not this one."
=> false
irb> chab "Maybe this? {0x35}"    # wrong kind of brackets
=> false
irb> chab "Or this? <0x38z7e>"    # bogus hex digit
=> false
irb> chab "Okay, this: <0xfc0004>."
=> true

=~ 是关于样式规则比对的运算符号;它回传符合样式之字串内容的位置,若无符合者则回传 nil

下列示范分组与分组变数:

'abc123xyz' =~ /([a-z]+)(\d+)([a-z]+)/
puts $1, $2
abc
123

'abc123xyz' =~ /([a-z]+)(?:\d+)([a-z]+)/
puts $1, $2
abc
xyz

第二次比对时,指示第二个分组不赋予编号,故 $2 之值成为第三个分组结果。

尽管字样规则式一眼看来像个谜语,你仍将很快地为它所能节省的许多表达功夫而感到满意。

阵列(Array)[编辑]

阵列是由多个个体所组成的聚合体。你可以用方括弧 ([ ]) 建立一个阵列 ,并在其中以逗号分隔列出各个细项。也可用 Array.new 建立一个空白阵列,或是使用 %w 的替用语法。Ruby 的阵列可以容纳各式各样的个体。

我们可以数字作为索引,提取阵列的任何一部分。负号指示自阵列尾端取偏移量而非从阵列开头。

array_one   = Array.new
array_two   = []              # shorthand for Array.new
array_three = ["a", "b", "c"] # array_three contains "a", "b" and "c"
array_four  = %w'a b c'       # array_four also contains "a", "b" and "c"
array_three[0]                # => "a"
array_three[2]                # => "c"
array_three[-2]               # => "b"
array_four[0]                 # => "a"
array_four[1,2]               # => ["b", "c"]

有二种快速建立阵列的方式。一种使用 %w 的替用语法;另一种是字串与阵列互转:字串可用split方法转成阵列,阵列可用join方法转成字串。

 array_one  = %w'apple orange pear'             # => ["apple", "orange", "pear"]
 array_two  = 'apple orange pear'.split         # => ["apple", "orange", "pear"]
 array_one == array_two                         # => true
 array_three = %w'dog:cat:bird'                 # => ["dog:cat:bird"]
 array_four = 'dog:cat:bird'.split(':')         # => ["dog", "cat", "bird"]
 array_three == array_four                      # => false
 string_one = array_one.join(',')               # => "apple,orange,pear"
 array_five = string_one.split(',')             # => ["apple", "orange", "pear"]
 array_five == array_one                        # => true

杂凑表(Hash)[编辑]

有种称为“关联索引阵列(associative array)”的特殊阵列,它不是以有序的数字索引提取内容,而是以无序的键值(keys) - 或称关键字。这种阵列有时又称之为杂凑表(hash)字典(dictionary);在 Ruby 的世界中,我们惯用杂凑表一词。我们可以用一对大括号 ({ }) 括起各对细项 (鍵值 => 內容) 以建立杂凑表;也可用 Hash.new 建立一个空白杂凑表。在编程时,用杂凑表的时机将比用阵列的时机要多。

hash_one   = Hash.new
hash_two   = {}                             # shorthand for Hash.new
hash_three = {"a" => 1, "b" => 2, "c" => 3} #=> {"a"=>1, "b"=>2, "c"=>3}

符号常被当成是杂凑表的键值(可以更快存取),所以您可能看到下列定义方式:

hash_sym   = { :a => 1, :b => 2, :c => 3}   #=> {:b=>2, :c=>3, :a=>1}

范围(Range)[编辑]

范围是一种表示所有预期值子集的型态。确切而言,所有预期值为处于起始值至终止值之间的任意值。范围有两种表达型式:

  1. 起始值..終止值: 意义为'起始值 <= 预期值 <= 终止值'
  2. 起始值...終止值: 意义为'起始值 <= 预期值 < 终止值' (不含终止值)

例如下列范围:

  • 所有介于 0 到 5 之间的整数。
  • 所有介于 0 到 1 之间的数 (含非整数) ,但不含 1 。
  • 所有介于 't' 到 'y' 之间的英文字母。

以 Ruby 表达如:

0..5
0.0...1.0
't'..'y'

范围的表达方式为递增,不是递减。所以下列写法错误,将得到一个空集合:

5..0

范围之集合仅可为相同类或有共同父祖类之实例,且必须可比较(Comparable),即实作了 <=> 方法。范围实际上为 Range 类之实例,故具有该类之行为。下列以===方法判断右值是否在范围之中:

r = 0..5
puts r === 4  # => true
puts r === 7  # => false

关于 Range 类所有方法之详细资讯,请参考Range class reference

符号(Symbol)[编辑]

将冒号(:)加在识别字前,即为符号。符号是一种识别用的记号,经常于杂凑表中作为键值。可用%s指定冒号之替用符号。

范例:

:abc    # just mark here as 'abc'
t1 = {:name => 'john', :age => 20}
puts t1[:name]
t2 = {%s'name' => 'smith', %s[age] => 16}
puts t2[:name]

一般而言,以符号作为杂凑表之键值时效能较佳。因为以字串为键值时,Ruby 必须计算字串的杂凑值;以符号为键值时则不需计算。熟悉 C/C++ 语言的程序员可以 enum 与 map 之差异比对:以符号为键值类似以 enum 建立表;以字串为键值类似以 map class 建立表。两者效能差异显著。

上一项: 变数与常数 索引 下一项: 运算子