跳轉到內容

Ruby 程式設計/語法/運算子

來自 Wikibooks,開放世界中的開放書籍

運算子

[編輯 | 編輯原始碼]

完整列表和優先順序

[編輯 | 編輯原始碼]
運算子 名稱/含義 引數 優先順序[1] 關聯
! 布林非 一元 1
~ 按位取反 一元 1
+ 一元加(無效果) 一元 1
** 求冪 二元 1
- 一元減(反轉符號) 一元 2
* 乘法 二元 3
/ 除法 二元 3
% 取模(餘數) 二元 3
+ 加法或連線 二元 4
- 減法 二元 4
<< 按位左移或追加(<< 和 <<- 也用於 "here doc" 符號 二元 5
>> 按位右移 二元 5
& 按位與 二元 6
| 按位或 二元 7
^ 按位異或 二元 7
< 小於 二元 8
<= 小於或等於 二元 8
> 大於 二元 8
>= 大於或等於 二元 8
== 等於(計算為相同的值) 二元 9 N/A
=== "案例相等"、"案例包含" 或 "三個等號" 運算子。A === B 如果 B 是 A 集合的成員。操作根據 A 和 B 的資料型別而有很大差異。 二元 9 N/A
!= 不等於 二元 9 N/A
=~ 模式匹配 二元 9 N/A
!~ 否定模式匹配 二元 9 N/A
<=> A <=> B 計算為 -1、0 或 1;如果 A 分別小於、等於或大於 B 二元 9 N/A
&& 布林與 二元 10
|| 布林或 二元 11
.. 範圍建立,布林觸發器 二元 12 N/A
... 開放式範圍建立,布林觸發器 二元 12 N/A
?: A?B:C 如果 A 為真,則計算為 B,否則計算為 C 三元 13
救援 用於捕獲異常的修飾符,例如 array[3] rescue nil 二元 14
= 賦值 二元 15
**= A **=B 做 A = A ** B 二元 15
*= A *=B 做 A = A * B 二元 15
/= A /=B 做 A = A / B 二元 15
%= A %=B 做 A = A % B 二元 15
+= A +=B 做 A = A + B 二元 15
-= A -=B 做 A = A – B 二元 15
<<= A <<=B 做 A = A << B 二元 15
>>= A >>=B 做 A = A >> B 二元 15
&&= A &&=B 如果 A 為真或不為 nil,則將 B 賦值給 A 二元 15
&= A &=B 做 A = A & B 二元 15
||= A ||=B 如果 A 為 nil 或假,則將 B 賦值給 A 二元 15
|= A |= B 做 A = A | B 二元 15
^= A ^=B 做 A = A ^ B 二元 15
defined? 如果表示式無法計算(例如未設定的變數),則為 nil 一元 16 N/A
not 布林非 一元 17
and 布林與 二元 18
or 布林或 二元 18
if 條件,例如 print x if x 二元 19 N/A
unless 否定條件,例如 x = 0 unless x 二元 19 N/A
while 迴圈條件,例如 print x += 1 while (x < 10) 二元 19 N/A
until 迴圈條件,例如 print x += 1 until (x == 10) 二元 19 N/A

優先順序較高的(在上面的表格中數字較小的)運算子首先對其直接引數進行計算。優先順序順序可以透過 () 塊進行更改。例如,因為 * 的優先順序高於 +,那麼
1 + 2 * 3 == 7
(1 + 2) * 3 == 9

關聯方向控制在同一行中出現多個具有相同優先順序的運算子時,哪些運算子首先對其引數進行計算。例如,因為 - 具有左關聯

1 – 2 – 3 == (1 – 2) – 3 == -1 – 3 == -4

而不是

1 – 2 – 3 == 1 – (2 – 3) == 1 - -1 == 0

因為 ** 具有右關聯

2 ** 3 ** 2 == 2 ** (3 ** 2) == 2 ** 9 == 512

而不是

2 ** 3 ** 2 == (2 ** 3) ** 2 == 8 ** 2 == 64

{} 塊的優先順序低於上述運算子,其次是 do/end 塊。使用 [] 的陣列訪問可以認為具有高於任何上述運算子的優先順序。

運算子 ** 到 !~ 可以被重寫(為新類定義,或為現有操作重新定義)。

請注意,rescue、if、unless、while 和 until 在用作單行中的修飾符時(如上面的示例)是運算子,但也可以用作關鍵字。

其他運算子

[編輯 | 編輯原始碼]

點運算子 . 用於在物件上呼叫方法,也稱為向物件傳送訊息。

Ruby 2.3.0 引入了安全導航運算子 &.,也稱為 "孤獨運算子"。[2] 這允許替換

x = foo && foo.bar && foo.bar.baz

x = foo&.bar&.baz

為雜湊和陣列引入了等效的 .dig() 方法

hash_variable.dig(:foo, :bar, :baz)
array_variable.dig(1, 0, 2)

是更安全的版本

hash_variable[:foo][:bar][:baz]
array_variable[1][0][2]

安全導航運算子如果請求的方法、鍵或索引不可用,將引發錯誤;與使用 try() 來實現此目的不同,後者將返回 nil。[3]

松本行弘在確定使用 &. 之前考慮了 ! ?..?,因為:[4]

  • ?. 與 *? 衝突
  • ?. 被其他語言使用,因此 .? 令人困惑地相似但不同
  • ! 與 "非" 邏輯衝突
  • ? 已經按照慣例用於返回布林值的函式
  • &. 使人聯想到正在替換的 && 語法

!! 有時會被看到,但這只是 ! 運算子兩次。它用於強制以下表達式計算為布林值。這種技術被認為是非慣用的,並且程式設計實踐不佳,因為存在更明確的方法來強制這種轉換(而這種轉換很少需要)。

Ruby 中的賦值使用等號 "=" 完成。這既適用於變數也適用於物件,但由於字串、浮點數和整數實際上是 Ruby 中的物件,因此您始終在分配物件。

示例

  myvar = 'myvar is now this string'
  var = 321
  dbconn = Mysql::new('localhost','root','password')

自我賦值

  x = 1           #=>1
  x += x          #=>2
  x -= x          #=>0
  x += 4          #=>x was 0 so x= + 4 # x is positive 4
  x *= x          #=>16
  x **= x         #=>18446744073709551616 # Raise to the power
  x /= x          #=>1

來自 C 和 C++ 型別的人經常問的一個問題是 "如何遞增變數?++ 和 -- 運算子在哪裡?" 在 Ruby 中,應該使用 x+=1 和 x-=1 來遞增或遞減變數。

  x = 'a'
  x.succ!         #=>"b" : succ! method is defined for String, but not for Integer types

多重賦值

示例

  var1, var2, var3 = 10, 20, 30
  var1           #=> 10
  var2           #=> 20
  var3           #=> 30

  myArray=%w(John Michel Fran Doug) # %w() can be used as syntactic sugar to simplify array creation
  var1,var2,var3,var4=*myArray
  puts var1           #=>John
  puts var4           #=>Doug

  names,school=myArray,'St. Whatever'
  names               #=>["John", "Michel", "Fran", "Doug"]
  school              #=>"St. Whatever"

條件賦值

  x = find_something() #=>nil
  x ||= "default"      #=>"default" : value of x will be replaced with "default", but only if x is nil or false
  x ||= "other"        #=>"default" : value of x is not replaced if it already is other than nil or false

運算子 ||= 是一種簡寫形式,它與表示式非常相似:[5]

 x = x || "default"

運算子 ||= 可以是類似於以下程式碼的簡寫

  x = "(some fallback value)" unless respond_to? :x or x

以同樣的方式 &&= 運算子起作用

  x = get_node() #=>nil
  x &&= x.next_node #=> nil : x will be set to x.next_node, but only if x is NOT nil or false
  x = get_node() #=>Some Node
  x &&= x.next_node #=>Next Node

運算子 &&= 是表示式的簡寫形式

 x && x = x.get_node()

作用域

[編輯 | 編輯原始碼]

在 Ruby 中,存在區域性作用域、全域性作用域、例項作用域和類作用域。

區域性作用域

[編輯 | 編輯原始碼]

示例

 var = 2
 4.times do |x|
   puts x = x*var
 end
 #=>
 0
 2
 4
 6
 puts x
 #=> undefined local variable or method `x' for main:Object (NameError)

此錯誤出現是因為此 x(頂層) 不是 do..end 塊內的 x(區域性),x(區域性) 是塊的區域性變數,而在嘗試 puts x(頂層) 時,我們呼叫的是頂層作用域中的 x 變數,由於不存在,Ruby 就會報錯。

全域性作用域

[編輯 | 編輯原始碼]
 $global = 0
 4.times do |var|
   $global = $global + var
   puts "var #{var}  global #{$global}"
 end
 #=>
 var 0  global 0
 var 1  global 1
 var 2  global 3
 var 3  global 6
 
 puts $global
 #=> 6

此輸出給出是因為在變數前面加上美元符號會使變數成為全域性變數。

例項作用域

[編輯 | 編輯原始碼]

在類的函式內部,可以透過在變數前面加上 @ 來共享變數。

 class A
   def setup
     @instvar = 1
   end
   def go
     @instvar = @instvar*2
     puts @instvar
   end
 end
 instance = A.new
 instance.setup
 instance.go
 #=> 2
 instance.go
 #=> 4

類作用域

[編輯 | 編輯原始碼]

類變數類似於 Java 中的 "靜態" 變數。它由類的所有例項共享。

 class A
   @@classvar = 1
   def go
     @@classvar = @@classvar*2
     puts @@classvar
   end
 end
 instance = A.new
 instance.go
 #=> 2
 instance = A.new
 instance.go
 #=> 4 -- variable is shared across instances

這是一個展示各種型別的演示

$variable
 class Test
   def initialize(arg1='kiwi')
     @instvar=arg1
     @@classvar=@instvar+' told you so!!'
     localvar=@instvar
   end
   def print_instvar
     puts @instvar
   end
   def print_localvar
     puts @@classvar
     puts localvar
   end
 end
 var=Test.new
 var.print_instvar              #=>"kiwi", it works because a @instance_var can be accessed inside the class
 var.print_localvar             #=>undefined local variable or method 'localvar' for #<Test:0x2b36208 @instvar="kiwi"> (NameError).

這將列印兩行“kiwi”和“kiwi told you so!!”,然後由於 #<Test:0x2b36208 @instvar="kiwi">(NameError)中未定義的區域性變數或方法“localvar”而失敗。為什麼呢? 嗯,在方法 print_localvar 的範圍內,不存在 localvar,它存在於方法 initialize 中(直到 GC 將其清除)。另一方面,類變數 '@@classvar' 和 '@instvar' 在整個類中都有效,並且對於 @@class 變數,在子類中也有效。

 class SubTest < Test
   def print_classvar
     puts @@classvar
   end
 end
 newvar=SubTest.new              #newvar is created and it has @@classvar with the same value as the var  instance of Test!!
 newvar.print_classvar           #=>kiwi told you so!! 

類變數的範圍包括父類和子類,這些變數可以在類之間存在,並且可以受到子類操作的影響;-)

 class SubSubTest < Test
   def print_classvar
     puts @@classvar
   end
   def modify_classvar
     @@classvar='kiwi kiwi waaai!!'
   end
 end
 subtest=SubSubTest.new
 subtest.modify_classvar          #lets add a method that modifies the contents of @@classvar in  SubSubTest
 subtest.print_classvar

Test 的這個新的子類也具有 @@classvar,其值為原始值 newvar.print_classvar。@@classvar 的值已更改為“kiwi kiwi waaai!!”。這表明 @@classvar 在父類和子類之間是“共享”的。

預設範圍

[編輯 | 編輯原始碼]

當你沒有將程式碼包含在任何範圍限定符中,例如

 @a = 33

它會影響預設範圍,即一個名為“main”的物件。

例如,如果你有一個指令碼是這樣的

@a = 33
require 'other_script.rb'

而另一個指令碼 other_script.rb 是這樣的

puts @a
 #=> 33

它們可以共享變數。

但是請注意,這兩個指令碼不共享區域性變數。

區域性範圍的陷阱

[編輯 | 編輯原始碼]

通常當你處於一個類中時,你可以根據需要進行定義,例如。

class A
  a = 3
  if a == 3

    def go
       3
    end
  else
    def go
       4
    end
  end

end

而且,procs 會“繫結”到它們周圍的範圍,例如

 a = 3
 b = proc { a }
 b.call # 3 -- it remembered what a was

但是,關鍵字“class”和“def”會建立一個*全新的*範圍。

class A
  a = 3
  def go
     return a # this won't work!
  end
end

你可以使用 define_method 來繞過這個限制,它接受一個塊並因此保留外部範圍(注意你可以使用任何你想要的塊,這裡舉個例子)。

class A
   a = 3
  define_method(:go) {
      a
  }
end

這裡使用一個任意的塊

a = 3
PROC = proc { a } # gotta use something besides a local
# variable because that "class" makes us lose scope.

class A
  define_method(:go, &PROC)
end

或者這裡

 class A
 end
a = 3
 A.class_eval do
   define_method(:go) do
       puts a
   end
end

邏輯與

[編輯 | 編輯原始碼]

二元“and”運算子將返回其兩個運算元的邏輯合取。它與“&&”相同,但優先順序較低。示例

a = 1
b = 2
c = nil
puts "yay all my arguments are true" if a and b
puts "oh no, one of my argument is false" if a and c

邏輯或

[編輯 | 編輯原始碼]

二元“or”運算子將返回其兩個運算元的邏輯析取。它與“||”相同,但優先順序較低。示例

a = nil
b = "foo"
c = a || b  # c is set to "foo" it's the same as saying c = (a || b)
c = a or b  # c is set to nil   it's the same as saying (c = a) || b which is not what you want.

參考資料

[編輯 | 編輯原始碼]
  1. http://ruby-doc.org/core-2.4.0/doc/syntax/precedence_rdoc.html
  2. https://www.ruby-lang.org/en/news/2015/12/25/ruby-2-3-0-released/
  3. http://blog.rubyeffect.com/ruby-2-3s-lonely-operator/
  4. https://bugs.ruby-lang.org/issues/11537
  5. http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html
華夏公益教科書