)理解 Ruby Symbol

來源:互聯網
上載者:User

Symbol 是什麼

  Ruby 是一個強大的物件導向指令碼語言(本文所用 Ruby 版本為1.8.6),在 Ruby 中 Symbol 表示“名字”,比如字串的名字,標識符的名字。建立一個 Symbol 對象的方法是在名字或者字串前面加上冒號:

建立 symbol 對象

:foo:test 

 

:”abc
:"I am a boy”

 

  你可能會問,字串就是字串,幹嗎還有字串的名字?這是因為在 Ruby 中字串也是一種對象,即 String 對象。無論其結構還是操作和 Symbol 對象都是不同的。在 Ruby 中每一個對象都有唯一的物件識別碼(Object Identifier),可以通過 object_id 方法來得到一個對象的標識符。我們來看看 Symbol 對象和 String 對象的差別:

Ruby 物件識別碼

irb(main):001:0> puts :foo.object_id327458=> nilirb(main):002:0> puts :foo.object_id327458=> nilirb(main):003:0> puts :"foo".object_id327458=> nilirb(main):004:0> puts "foo".object_id24303850=> nilirb(main):005:0> puts "foo".object_id24300010=> nilirb(main):006:0> puts "foo".object_id24296170=> nil

 

  可以看到,前三行語句中的 :foo (或者 :"foo")都是同一個 Symbol 對象,其 object id 為327458,而後三行中的字串”foo”都是不同的對象,其 object id 依次為24303850、24300010、24296170。

  可見,每個 String 對象都是不同的,即便他們包含了相同的字串內容;而對於 Symbol 對象,一個名字(字串內容)唯一確定一個 Symbol 對象。

值得注意的是建立 Symbol 對象的字串中不能含有’\0’字元,而 String 對象是可以的。

非法 Symbol 字串

irb(main):001:0>  :"fo\0o"SyntaxError: compile error(irb):1: symbol cannot contain '\0'        from (irb):1irb(main):002:0> :"foo\0"SyntaxError: compile error(irb):2: symbol cannot contain '\0'        from (irb):2irb(main):003:0> puts "foo\0".object_id24305140=> nilirb(main):004:0> puts "fo\0o".object_id24301000=> nilirb(main):005:0>

 

  除了可以採用一般的字串,還可以使用操作符(例如+, -, *, /),變數,常量,方法甚至類的名字來建立 Symbol 對象,例如:+就是一個合法的 Symbol 。實際上,在 Ruby 內部操作符、變數等名字本身就是作為 Symbol 處理的,例如當你定義一個執行個體變數時, Ruby 會自動建立一個 Symbol 對象,例如 @test 對應為 :@test 。

執行個體變數的 Symbol

class Test   attr_accessor :testend

 

  這個類定義了一個具有讀寫方法的執行個體變數 @test 。實際上 Ruby 建立了兩個 Symbol ,一個是執行個體變數的 symbol :@test ,另一個是 :test 。那如果使用字串對象 ”test” 作為參數呢?也可以,仍然會建立兩個 symbol ,:test 和 :@test ,為什麼還會建立 :test 呢?這是和Ruby的實現相關的(至少Ruby1.8.6裡是這樣)。

注意,類變數 @@test 和執行個體變數 @test 對應的 Symbol 顯然是不同的。記住:名字相同,則Symbol 相同

名字相同, Symbol 相同

class Test    puts :Test.object_id  Test = 10  puts :Test.object_id    def Test      puts :Test.object_id  end  end Test.new.Test

運行結果

224298224298224298

 

名字不同, Symbol 不同

class Test    puts :Test.object_id  @@test = 10  puts :@@test.object_id  def test      puts :test.object_id    @test = 10      puts :@test.object_id  end  end t =Test.new  t.test  

運行結果

22429828806879858288108

 

  第一個例子裡,類名、常量名和方法名都是 Test ,因此相應的 Symbol 對象都是 :Test 。不用擔心, Ruby 可以很好區分它在不同上下文中到底表示什麼。當然這並不是一個好的編程風格,但對於理解 Ruby 的 Symbol 還是有協助的: Symbol 表示一個名字,僅此而已。

  Symbol 對象一旦定義將一直存在,直到程式執行退出。所有 Symbol 對象存放在 Ruby 內部的符號表中,可以通過類方法 Symbol.all_symbols 得到當前 Ruby 程式中定義的所有 Symbol 對象,該方法返回一個 Symbol 對象數組。由於 Symbol 比較多,你可以 dump 到檔案中來查看。

all_symbols 方法

irb(main):001:0> Symbol.all_symbols.size=> 4047irb(main):002:0> Symbol.all_symbols[0..9]=> [:@level_notifier, :ppx, :msg_dn, :version, :secs, :@user, :pos, :socketpair, :TkENSURE, :HTTPAccepted]irb(main):003:0> File.open("sym", "w") do |file| file.puts Symbol.all_symbols end=> nil

 

Symbol 和 String

  Symbol 對象和 String 對象是完全不同的東西,物件識別碼很明確的說明了這一點。除此之外,我們還可以從兩種對象的方法上區分。查看 Ruby 庫參考,你會發現 String 類有非常多的方法,包括 Mixed-in 方法(Ruby中一個類通過 include 其他模組而得到的方法,實現多重繼承的效果)、類方法和執行個體方法;而 Symbol 類只有一個類方法 all_symbols 和7個執行個體方法。例如,可以通過 []= 方法改變 string 的內容,而 symbol 則不行:

[]= 方法比較

irb(main):001:0> s="test"=> "test"irb(main):002:0> s[0]='1'=> "1"irb(main):003:0> puts s1est=> nilirb(main):004:0> sym=:test=> :testirb(main):005:0> sym[0]=1NoMethodError: undefined method `[]=' for :test:Symbol        from (irb):5irb(main):006:0>

 

  雖然 Symbol 和 String 是不同的對象,但它們之間關係很密切。 Ruby 提供了方法在 Symbol和 String 之間轉換。Symbol 轉化為 String,使用 to_s 或 id2name 方法將 Symbol 轉化為一個 String 對象:

Symbol 到 String

irb(main):001:0> :test.id2name=> "test"irb(main):002:0> :test.to_s=> "test"irb(main):003:0> :"I am a boy".to_s=> "I am a boy"

  

  注意,每個 String 對象都是唯一的,因此對一個 Symbol 調用多次將產生多個 String 對象。

  String 轉化為 Symbol,除了在字串前面加冒號,還可以使用 to_sym 或 intern 方法將 String 轉化為 Symbol ,如果該 Symbol 已經存在,則直接返回。

String 到 Symbol

irb(main):001:0> var1 = "test".to_sym=> :testirb(main):002:0> var2 = "test".intern=> :testirb(main):003:0> var1 == var2=> trueirb(main):004:0>

 

使用 Symbol

  正如前邊提到的, Ruby 內部一直在使用 Symbol ,比如 Ruby 程式中的各種名字,Symbol本質上是 Ruby 符號表中的東西。使用 Symbol 處理名字可以降低 Ruby 記憶體消耗,提高執行速度,這點我們在下一篇文章中會看到。

  那麼 Symbol 對我們有什麼用呢?當然也是記憶體。使用 String 的開銷太大了,因為每一個String 都是一個對象。想想前邊的例子,一個字串每出現一次 Ruby 就會建立一個 String 對象。

  通常來講,當你面臨 String 還是 Symbol 的選擇時,可以參考以下標準:

  • 如果使用字串的內容,這個內容可能會變化,使用 String
  • 如果使用固定的名字或者說是標識符,使用 Symbol

  那麼什麼時候我們會用到名字呢?很多時候都會,比如枚舉值、關鍵字(雜湊表關鍵字、方法的參數)等等;作為雜湊表的 key,雜湊表是 Symbol 應用最為廣泛的地方。

在ruby中,雜湊和數組類似,一個雜湊表是一系列 key/value 對的集合,只不過它的 key 取值範圍更廣泛,可以是任何對象,比如Regex。但通常我們都會取有意義的 key ,比如 String、Symbol 。

下面這個雜湊表表示按城市分類的一些機器的集合。

一個雜湊表例子

hosts{       'beijing' => 'machine1',       'shanghai'  => 'machine2',       'guangzhou' => 'machine3',       'tianjin' =>  'machine4',       'shenzhen' => 'machine5'}

  

  如果要引用 beijing 的機器,使用 hosts['beijing'] 。但如果我們程式中要頻繁引用雜湊表中 value ,這樣就不大好了,因為 Ruby 對每一次字串引用都會產生一個 String 對象,累積下來這個開銷是相當大的。

我們完全可以使用 Symbol ,因為對於這些 key 來講,我們用的就是名字而已,例如下面hosts[:beijing]

 

使用 Symbol 作為 key

hosts = { :beijing => 'machine1', :shanghai => 'machine2', :guangzhou => 'machine3', :tianjin  => 'machine4', :shenzhen => 'machine5'}

 

雜湊參數

  通常我們定義的函數的參數的個數和順序是寫死的,調用函數的時候要確保參數的個數、順序匹配,有時候這樣很不方便,使用雜湊參數可以解決這個問題。ROR 中就大量地運用這種方式,也許你已經看到了,到處都是 Symbol 和雜湊。比如:

使用雜湊參數的方法調用

link_to 'Show', :action => 'show', :id => productadd_column :products, :price, :decimal, :precision => 8, :scale => 2, :default => 0

使用雜湊參數的方法可以如下定義,前半部分為固定參數,後面為可變參數,或者乾脆全採用雜湊參數:

雜湊參數

def my_method(para1, …, options={})#your codeenddef my_method(options={})#your codeend

   

  如果你希望設定一些預設參數,並允許調用者更改這些參數,可以使用雜湊對象的 merge! 方法, hsh.merge!( other_hash )該方法將 other_hash 裡內容加到 hsh 中,如果other_hash hsh 有重複的 key ,則 key other_hash 中的 value 覆蓋 hsh 中對應 key value

方法定義-使用預設參數

class Test def my_method(opts={})  default_opts={:arg1 => 10, :arg2 => "abc"}  default_opts.merge!(opts)  default_opts.each{|key,value| puts "#{key} is #{value}"} endendt = Test.newt.my_method :arg1=>5, :arg3=>"def"

運行結果

arg1 is 5arg2 is abcarg3 is def

 

 

 

註:本文轉自:http://www.ibm.com/developerworks/cn/opensource/os-cn-rubysbl/, 詳細資料請訪問文章出處,本文僅供學習之用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.