Ruby學習筆記之Ruby 對象

來源:互聯網
上載者:User

大部分 Ruby 程式,它們的設計,邏輯,動作,都是圍繞著對象進行的。寫一個 Ruby 程式,主要的工作就是去建立對象,然後給它們能力,讓它們可以去執行動作。

Ruby 是 OOP 語言,就是物件導向的語言,你執行的計算,資料處理,輸入與輸出這些動作,都是通過建立對象,然後讓這個對象去執行指定的動作來完成的。對象(object)在現實世界裡,就是一個東西。一個蘋果是一個對象,一張桌子也是一個對象。

每個對象是一個特定的類的執行個體(instance),對象的行為大部分是在它們所屬的那個類上面定義的方法(method)決定的。
當你想幹點什麼的時候,比如計算,輸出,資料比較,你就是讓一個對象去做。比如你想問 a 是否等於 b ,你要問的是 a 是不是認為它自己等於 b 。如果你想知道一個學生是不是上了某個老師的課,你可以問這個學生,“你是不是這個老師的學生”。

寫 Ruby 程式,大部分工作就是建立對象,讓每個對象都扮演個角色,每個對象可以執行跟這個角色相關的動作。

建立一個通用對象

類(class)是一種捆綁與標記行為的方法,它讓建立多個擁有相似行為的對象更簡單一些。在 Ruby 裡面,每個對象都有可能去學習一些行為(method),這些行為可能不在它們的類裡面。類的概念適用在對象概念之上,而不是反過來的,所以我們先瞭解一下對象。

建立一個通用的對象,它不表示某個具體的東西:

obj = Object.new
上面就是建立了一個對象,這個對象交給了變數 obj,你可以通過這個變數來處理新建立的這個對象。

>> obj = Object.new
=> #<Object:0x007fb839154e80>
所有的 Ruby 對象天生會具有某些能力,就是它會擁有一些可以執行的方法。我們也可以教會對象你想要讓它做的事情。

定義對象的行為

假設你想讓一個對象會說話,你得先讓它說話,在你讓它說話之前,你得先教會它說話。就是在對象上去定義方法(method),可以使用 def 這個關鍵詞去定義方法。

def obj.talk
  puts '我是個對象'
  puts '你是嗎?'
end
給對象發送資訊

讓 obj 這個對象可以說話,執行:

obj.talk
會返回:

我是個對象
你是嗎?
對象 obj 現在能明白或者能響應發送給它的 talk 這個資訊,如果對象上有一個跟這個資訊對應的方法,對象就會執行這個方法。

點(.)是一個資訊發送的操作符,點右邊的資訊會發送給點左邊的對象。
資訊的那個授受者一般就是一個使用變數表示的對象,也可以是字面構造的對象,比如使用引號的字串。
發送的資訊大部分情況下就是方法的名字,比如之前的那個 talk。對象會嘗試運行跟資訊對應的那個方法,如果方法不存在的話,就會觸發錯誤處理(error-handling)。
帶參數的方法

在調用方法的時候,你可以為方法提供一個或多個參數值。在定義方法的時候,參數就是在方法名字右邊出現的在一個括弧裡的一個列表的變數,參數可以是必須的,也可以是可選的。調用方法的時候,你可以為這些變數提供值。

在定義方法的時候,列出的那些變數,它們是方法的 formal parameters(形式參數)。在調用方法的時候,你為這些變數提供的具體的值是方法的 arguments (實際參數)。一般我們就可以使用 arguments 來表示方法的參數,包含形式參數與實際參數。中文就可以直接叫參數。

比如一個攝氏溫度轉華氏溫度的方法:

def obj.c2f(c)
  c * 9.0 / 5 + 32
end
上面方法支援一個參數 c ,調用這個方法的時候可以為這個參數提供值:

puts obj.c2f(100)
結果是:

212.0
定義與調用方法的時候括弧是可選的東西。

def obj.c2f c
obj.c2f 100
方法的傳回值

Ruby 的運算式都會有一個值,下面是一些運算式,還有它們的值。

>> 2 + 2
=> 4
>> 'hello'
=> "hello"
>> 'hello' + ' there'
=> "hello there"
>> c = 100
=> 100
>> c * 9/5 + 32
=> 212
>> obj.c2f(100)
=> 212.0
503CF7F1-4C64-4ABC-98C5-2C9E5886BBE2

每個方法的調用是一個運算式。調用一個方法的時候,方法會算出一些東西,這個算出來的結果就是方法的返回的值(return value)。

在定義方法和時候,也可以使用 return 這個關鍵詞去傳回值:

def obj.c2f(c)
  return c * 9.0 / 5 + 32
end
上面這個例子跟之前我們定義的方法是一樣的,加上 return 更清楚一些,使用 return 可以返回多個值,比如 return a,b,c ,返回的值的列表會自動放到一個數組裡面。不管用不用 return,每個方法的調用都會返回某些東西。甚至一個空白主體的方法,它都會返回一個 nil 。

建立一個表示票的對象

一張票(ticket)可以提供關於它自己的一些資料,比如這個劇的時間,位置,名字是什麼,表演者是誰,哪個座位,多少錢等等。

01/02/03
Town Hall
Author's reading
Mark Twain
Second Balcony, row J, seat 12
$5.50
建立對象

建立一個對象可以很容易的得到上面這些資訊。

ticket = Object.new
有了 ticket 這個對象以後,在它裡面再去定義一些方法:

def ticket.date
  '01/02/03'
end

def ticket.venue
  'Town Hall'
end

def ticket.event
  'Author's reading'
end

def ticket.performer
  'Mark Twain'
end

def ticket.seat
  'Second Balcony, row J, seat 12'
end

def ticket.price
  5.50
end
現在 ticket 對象就知道了一些關於自己的事情。

查詢對象

想得到 ticket 對象的時間,可以執行 ticket.date ,得到座位,執行 ticket.seat。

字串插值

string interpolation,就是在一個字串裡插入值。用的形式是 #{表示值的東西} ,比如 #{ticket.event} 。

puts "This ticket is for: #{ticket.event}, at #{ticket.venue}." +
 "The performer is #{ticket.performer}." +
 "The seat is #{ticket.seat}, " +
 "and it costs $#{"%.2f." % ticket.price}"
方法裡的布爾值

一張票是不是已經賣掉了,一種方法是可以給它添加一個可用的狀態:

def ticket.availability_status
  "sold"
end
還有一種方法是問一下它是不是可用,它會返回 true 或者 false:

def ticket.available?
  false
end
true 與 false 在 Ruby 裡面都是對象,使用它們可以在方法裡表示 真 或者 假。注意 available?,這裡有個問號,它會算出 true 或 false,這樣在使用這種方法的時候有點像是問一個問題:

if ticket.available?
  puts "You're in luck!"
else
  puts "Sorry--that seat has been sold."
end
Ruby 裡的每個運算式都會得到一個對象,這個對象裡面都會有一個表示真假的值,這個值幾乎在所有的 Ruby 對象裡都是 true 。只有 false 跟 nil 對象裡的表示真假的值是 false。

ABBC0D70-3A82-47DF-A2E0-50AE1CD5ABC6

>> if 'abc'
>>   puts '在 Ruby 裡字串是 true'
>> end
(irb):105: warning: string literal in condition
在 Ruby 裡字串是 true
=> nil
>> if 123
>>   puts '數字也是 true'
>> end
數字也是 true
=> nil
>> if 0
>>   puts '0 也是 true,注意 0 在某些語言裡不是 true'
>> end
0 也是 true,注意 0 在某些語言裡不是 true
=> nil
>> if 1 == 2
>>   puts '1 不等於 2,所以不會顯示這個字串'
>> end
=> nil
對象天生的行為

2016年9月5日 下午1:11 ****

一個對象存在以後它就具有了一些天生的行為,查看這些天生的行為,執行一下:

>> puts Object.new.methods.sort
!
!=
!~
<=>
==
===
=~
__id__
__send__
class
clone
define_singleton_method
display
dup
enum_for
eql?
equal?
extend
freeze
frozen?
hash
inspect
instance_eval
instance_exec
instance_of?
instance_variable_defined?
instance_variable_get
instance_variable_set
instance_variables
is_a?
itself
kind_of?
method
methods
nil?
object_id
private_methods
protected_methods
public_method
public_methods
public_send
remove_instance_variable
respond_to?
send
singleton_class
singleton_method
singleton_methods
taint
tainted?
tap
to_enum
to_s
trust
untaint
untrust
untrusted?
=> nil
對象的 ID

每個對象都有一個 ID,這個 ID 會在 object_id 裡面,比如:

>> 'hello'.object_id
=> 70214603696960
再做個測試:

>> a = Object.new
=> #<Object:0x007fb83904f8c8>
>> b = a
=> #<Object:0x007fb83904f8c8>
>> a.object_id
=> 70214603668580
>> b.object_id
=> 70214603668580
再做個測試:

>> string_1 = 'hello'
=> "hello"
>> string_2 = 'hello'
=> "hello"
>> string_1.object_id
=> 70214603633100
>> string_2.object_id
=> 70214603619360
string_1 與 string_2 表示的字串都是 hello,但是它倆的 object_id 是不一樣的。

查詢對象是否能做什麼

先試一下:

>> obj = Object.new
=> #<Object:0x007fb8390262e8>
>> obj.talk
NoMethodError: undefined method `talk' for #<Object:0x007fb8390262e8>
 from (irb):128
 from /usr/local/bin/irb:11:in `<main>'
建立一個對象,試著給它發送 talk 這個資訊,Ruby 會提示 talk 這個方法還沒定義。這樣再試一下:

obj = Object.new

if obj.respond_to?("talk")
  obj.talk
else
  puts "Sorry, the object doesn't understand the 'talk' message."
end
建立一個對象,使用對象的 respond_to? ,問一下它能不能 talk 。respond_to? 是對象天生有的能力,它可以查詢出對象是不是可以對某些資訊做出響應。

使用 send 方法發送資訊

之前建立的那個票對象,假設你想根據使用者輸入的東西,來返回對應的關於這個對象的資訊,代碼像這樣:

print "Information desired: "
request = gets.chomp
然後你可以做判斷:

if request == "venue"
  puts ticket.venue
elsif request == "performer"
  puts ticket.performer
...
上面的方法有點長,你也可以這樣來做:

if ticket.respond_to?(request)
  puts ticket.send(request)
else
  puts "no such information"
end
上面用 respond_to? 問一下輸入的東西有沒有,也就是對象是不是可以對這個資訊做出響應,如果是的話,就用 send 這個方法去發送這個資訊,也就是讓對象去執行對應的方法。

方法的參數

Ruby 裡的方法可以包含零個或多個參數。參數的數量也可以是可變的。

必須與可選

先做個實驗:

obj = Object.new

def obj.one_arg(x)
  puts "I require one and only one argument!"
end

obj.one_arg(1,2,3)
結果會是:

ArgumentError: wrong number of arguments (3 for 1)
意思就是參數的數量錯了,應該只有一個參數,你確給出了三個。

在方法裡添加多個參數,可以使用 * 號,像這樣:

def obj.multi_args(*x)
  puts "I can take zero or more arguments!"
end
*x 這個參數的意思是,你調用這個方法的時候可以添加任意數量的參數。這個參數的值會是一個數組。

再看一個混合使用參數的例子:

def two_or_more(a,b,*c)
  puts "I require two or more arguments!"
  puts "And sure enough, I got: "
  p a, b, c
end
如果像這樣調用上面這個方法:two_or_more(1,2,3,4,5) ,得到的東西應該像這樣:

I require two or more arguments!
And sure enough, I got:
1
2
[3, 4, 5]
方面裡的 p 是輸出東西用的,要輸出的是傳遞給方法裡的 a,b,c 這幾個參數的值。調用方法的時候傳遞的參數是1,2,3,4,5,這樣 a 參數的值應該是 1 ,b 參數的值是 2 ,剩下的是 c 參數的值,它會是一個數組,裡麵包含了 3,4,5  。

參數的預設值

定義方法的時候可以為參數設定預設的值,如果調用方法的時候沒有設定參數的值,就會使用它的預設的那個值。

def default_args(a,b,c=1)
  puts "Values of variables: ",a,b,c
end
如果像這樣調用上面這個方法:

default_args(3,2)
得到的結果是:

Values of variables:
3
2
1
調用方法的時候我們沒給 c 參數傳遞值,這樣就會使用它的預設的值,也就是 1 。

參數的順序

先看個例子:

def mixed_args(a,b,*c,d)
  puts "Arguments:"
  p a,b,c,d
end

mixed_args(1,2,3,4,5)
調用方法返回的結果像這樣:

Arguments:
1
2
[3, 4]
5
*c 是一個海綿參數,它的優先順序比較低,所以如果這樣調用方法:

mixed_args(1,2,3)
返回的結果會是:

1
2
[]
3
下面是更詳細的參數例子:

必須
def m(abc)
  m(1,2,3)
    a = 1, b = 2, c = 3

可選
def m(*a)
  m(1,2,3)
    a = [1,2,3]

預設
def m(a=1)
  m
    a = 1
  m(2)
    a = 2

必須/可選
def m(a, *b)
  m(1)
    a = 1, b = []

必須/預設
def m(a,b=1)
  m(2)
    a = 2, b = 1
  m(2,3)
    a = 2, b = 3

預設/可選
def m(a=1, *b)
  m
    a = 1, b = []
  m(2)
    a = 2, b = []

必須/預設/可選
def m(a,b=2,*c)
  m(1)
    a = 1, b = 2, c = []
  m(1,3)
    a = 1, b = 3, c = []
  m(1,3,5,7)
    a = 1, b = 3, c = [5,7]

必須/預設/可選/必須
def m(a,b=2,*c,d)
  m(1,3)
    a = 1, b = 2, c = [], d = 3
  m(1,3,5)
    a = 1, b = 3, c = [], d = 5
  m(1,3,5,7)
    a = 1, b = 3, c = [5], d = 7
  m(1,3,5,7,9)
    a = 1, b = 3, c = [5, 7], d = 9

下午2:15 ****

本地變數與變數分配

下午2:15 ****

本地變數可以使用小寫字母或底線開頭,也可以包含數字,下面都是合格的本地變數名:

x
_x
name
first_name
plan9
user_ID
_
本地變數的意思就是它的作用範圍會受到限制,每個本地變數只會在程式的某一塊兒地方起作用。比如在程式的不同的地方可能會包含同樣名字的兩個本地變數。

常見的作用範圍就是在一個方法的定義裡面,看個例子:

def say_goodby
 x = 'goodbye'
 puts x
end

def start_here
 x = 'hello'
 puts x
 say_goodby
 puts 'Let us check whether x remained the same:'
 puts x
end

start_here
執行的結果應該是:

hello
goodbye
let us check whether x remained the same:
hello
第一行輸出的是 start_here 裡定義的 x 變數的值,也就是 hello ,第二行內容是在 start_here 裡面執行了 say_goodby,輸出了這個方法裡定義的 x 的值,最後在 start_here 裡輸出的 x 的值,仍然是在這個方法內部定義的那個 x 的值。

變數,對象,引用

先試一下:

str = 'hello'
現在 str 這個變數表示的就是字串 hello 。

再試一下:

str = 'hello'
abc = str
puts abc
str 表示 hello,把 str 的值分配給了 abc ,所以輸出 abc 的時候,結果就會是 hello 。

再試一下:

str = "hello"
abc = str
str.replace("goodbye")
puts str
puts abc
def say_goodbye
  str = "hello"
  abc = str
  str.replace("goodbye")
  puts str
  puts abc
end

say_goodbye
輸出的結果是:

goodbye
goodbye
第一個 goodbye 是 str 的,第二個是 abc 的,但是我們只替換了 str ,為啥 abc 的值也被替換了?

引用

在 Ruby 裡,大部分情況下變數本身不存對象的值,上面的 str 其實不包含 hello 這個字串,它只包含了一個對這個字串對象的引用。

在一個分配裡,左邊是變數,右邊是一個對象,變數會收到一個對這個對象的引用。如果分配的是一個變數到另一個變數,比如 abc = str,左邊的變數會複製一份右邊的變數的引用,也就是這兩個變數引用的會是同一個對象。

str.replace("goodbye")
把 str 引用的那個字串替換成了 goodbye ,變數 abc 引用了同樣的字串對象,所以在 str 上做的替換動作也會影響到 abc ,也就是字串的內容變成了 goodbye 。

不被引用的東西

在 Ruby 裡,有些對象會作為立即值儲存在變數裡。比如整數,符號(symbol,看起來像 :this,字串的前面帶個冒號),還有 true,false,nil 。你把這些值分配給一個變數的時候,變數會儲存這個值本身,而不是一個到它們的引用。Ruby 會自動判斷是否使用引用。

任何錶示立即值的對象,永遠都是唯一的一個對象,不管它分配給了多少個變數。比如只有一個 100 對象 ,只有一個 false 對象。

Ruby 的對象必須有一個或多個引用指向它,如果沒有引用,會釋放掉對象佔用的記憶體。

如果你有兩個或兩個以上的變數指向了同一個對象,你可以使用這些變數裡的其中的任何一個來發送資訊給它們表示的對象。

分配與重新分配變數中的引用

對變數的每次分配,都會把變數之前表示的東西清除掉。來試一下:

str = "hello"
abc = str
str = "goodbye"
puts str
puts abc
這回輸出的是:

goodbey
hello
引用與方法參數

來段代碼:

def change_string(str)
  str.replace("新內容")
end
再建立一個字串,把它發送給 change_string :

s = "原始內容"
change_string(s)
再檢查一下:

puts s
結果會是:

新內容
如果你不想改變那個字串,可以這樣試一下:

s = "Original string content!"
change_string(s.dup)
puts s
dup 方法會複製一個新的對象。也可以這樣做:

s = "原始內容"
s.freeze
change_string(s)
puts s
本地變數和跟它們很像的東西

Ruby 看到這些東西:s,ticket,puts,user_name,會把它們解釋成下面這三樣東西其中的一種:

本地變數:local variable
關鍵詞:keyword
方法調用:method call
關鍵詞是 Ruby 的保留詞,你不能把它們作為變數的名字。比如 def,if 這些都是關鍵詞。跟本地變數差不多,方法調用也是純文字,如果方法有參數,加上括弧以後很容易知道它們不是一個變數。如果調用的方法沒有括弧,Ruby 會整明白它到底是個啥。

Ruby 是這樣判斷標識符的:

如果標識符是關鍵詞,它就是一個關鍵詞。
如果有個等號在標識符的右邊出現,它就是一個本地變數。
其它的情況標識符就會是一個方法的調用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.