## 7.哈希和符號
哈希是具有程序或用戶而不是 Ruby 解釋器定義的索引的數組。 讓我們看一個程序,以了解哈希是如何工作的。 在文本編輯器中輸入以下程序( [hash.rb](code/hash.rb) )并執行它。
```rb
#!/usr/bin/ruby
# hash.rb
mark = Hash.new
mark['English'] = 50
mark['Math'] = 70
mark['Science'] = 75
print "Enter subject name:"
sub = gets.chop
puts "Mark in #{sub} is #{mark[sub]}"
```
輸出 1
```rb
Enter subject name:Math
Mark in Math is 70
```
輸出 2
```rb
Enter subject name:French
Mark in French is
```
看一下輸出 1。程序要求用戶輸入主題名稱。 當用戶輸入`Math`時,程序將給出數學標記。 讓我們遍歷代碼。 一開始,我們有`mark = Hash.new`行,在這一行中,我們聲明了一個稱為 hash 類型的標記的變量。 看一下以下幾行
```rb
mark['English'] = 50
mark['Math'] = 70
mark['Science'] = 75
```
與數組不同,哈希可以將對象作為索引。 在這種情況下,我們使用簡單的字符串作為索引。 我們將在英語,數學和科學中獲得的分數放在`mark['English']`,`mark['Math']`和`mark['Science']`中。 接下來的兩個語句
```rb
print "Enter subject name:"
sub = gets.chop
```
提示用戶輸入標記,當他輸入標記時,它將被存儲到名為`sub`的變量中。 在最后一行`puts "Mark in #{sub} is #{mark[sub]}"`中,我們簡單地使用`sub`作為鍵訪問哈希值并打印出來。
看一下輸出 2,在這種情況下,我輸入了`French`,程序沒有給出任何結果。 在下面的程序中,我們將學習如何處理它。
### 7.1。 散列中的默認值
當我們將索引傳遞給散列并且如果其值確實存在時,則散列將忠實地返回該值。 如果索引沒有定義值怎么辦。 在前面的示例中,我們看到該程序未返回任何內容。 我們希望在此程序中對其進行修復。 看這段代碼`mark = Hash.new 0`。 在其中,我們給出了`mark = Hash.new 0`,而不是像上一個那樣給出`mark = Hash.new`,這里零是默認值。 現在讓我們運行程序,看看會發生什么。
```rb
#!/usr/bin/ruby
# hash_default_value.rb
mark = Hash.new 0 # We specify default value of mark is zero
mark['English'] = 50
mark['Math'] = 70
mark['Science'] = 75
print "Enter subject name:"
sub = gets.chop
puts "Mark in #{sub} is #{mark[sub]}"
```
輸出量
```rb
Enter subject name:Chemistry
Mark in Chemistry is 0
```
查看輸出,我們尚未為`mark['Chemistry']`定義值,但是當主題名稱指定為“化學”時,結果為 0。 之所以如此,是因為我們將零設置為默認值。 因此,通過設置默認值,我們將獲得尚未定義的那些索引的值。
### 7.2。 循環散列
數組中的循環非常容易,我們通常使用數組中的每個函數來迭代數組中的對象。 以類似的方式,我們可以循環散列。 在文本編輯器中輸入以下代碼 hash_looping.rb 并執行它。
```rb
#!/usr/bin/ruby
# hash_looping.rb
mark = Hash.new 0 # We specify default value of mark is zero
mark['English'] = 50
mark['Math'] = 70
mark['Science'] = 75
total = 0
mark.each { |key,value|
total += value
}
puts "Total marks = "+total.to_s
```
Output
```rb
Total marks = 195
```
在上面的程序中,我們計算了哈希`mark`中存儲的所有標記的總數。 注意我們如何使用`each`循環。 請注意,我們在循環體中使用`|key,value|`獲得了鍵值對。 `key`保留哈希的索引,`value`保留存儲在該特定索引 <sup class="footnote">[ [25](#_footnotedef_25 "View footnote.") ]</sup> 處的值。 每次執行循環時,我們都將值加到 total,因此最后,變量 total 獲得了散列中存儲的值的總數。 最后,我們打印出總數。
下面是另一個將學生標記存儲在哈希中的程序,我們使用 each 循環打印與`key`對應的`key`和`value`。 希望你已經足夠了解,可以自行解釋以下代碼。
```rb
#!/usr/bin/ruby
# hash_looping_1.rb
mark = Hash.new 0 # We specify default value of mark is zero
mark['English'] = 50
mark['Math'] = 70
mark['Science'] = 75
total = 0
puts "Key => Value"
mark.each { |a,b|
puts "#{a} => #{b}"
}
```
Output
```rb
Key => Value
Science => 75
English => 50
Math => 70
```
### 7.3。 散列創建的更多方法
還有另一種創建哈希的方法,讓我們來看一下。 參見下文,該程序的解釋與先前程序 [hash_looping_1.rb](code/hash_looping_1.rb) 的解釋相同,不同之處在于以下程序中的突出顯示的行。 希望你能自己解釋一下這些程序。
```rb
#!/usr/bin/ruby
# hash_creation_1.rb
marks = { 'English' => 50, 'Math' => 70, 'Science' => 75 }
puts "Key => Value"
marks.each { |a,b|
puts "#{a} => #{b}"
}
```
Output
```rb
Key => Value
Science => 75
English => 50
Math => 70
```
### 7.4。 使用符號
通常在哈希中,我們使用符號作為鍵而不是字符串。 這是因為與 String 相比,Symbol 占用的空間要少得多。 速度和空間要求的差異現在可能對你并不明顯,但是如果你編寫的程序會創建成千上萬的哈希,則可能會付出巨大的代價。 因此,請嘗試使用符號代替字符串。
那么什么是符號? 通過在終端中輸入`irb --simple-prompt`來激活我們的 irb。 在其中鍵入以下內容
```rb
>> :x.class
=> Symbol
```
注意,我們在`x`之前放置了一個冒號,從而使其成為`:x`。 符號以冒號開頭。 當我們問`:x`是什么類時,它說它是`Symbol`。 符號是可以在哈希 <sup class="footnote">[ [26](#_footnotedef_26 "View footnote.") ]</sup> 中用作`key`的事物或對象。 以類似的方式,我們聲明另一個稱為`:name`的符號,并查看其所屬的類。
```rb
>> :name
=> :name
>> :name.class
=> Symbol
```
變量可以在其中包含符號。 請注意,下面我們為變量`a`分配了值`:apple`,該變量只不過是一個符號。 當我們通過使用`a.class`詢問它是`a`是什么類時,它說出它是一個符號。
```rb
>> a = :apple
=> :apple
>> a.class
=> Symbol
```
可以使用`to_s`方法將符號轉換為字符串。 請看下面的 irb 示例,我們將符號轉換為字符串。
```rb
>> :orange.to_s
=> "orange"
```
要將字符串轉換為符號,請使用`.to_sym`方法,如下所示
```rb
>> "hello".to_sym
=> :hello
```
讓我們編寫一個程序,在該程序中,哈希不使用 String 而是使用 Symbols 作為鍵。 在文本編輯器中輸入下面的程序 hash_symbol.rb 并執行它。
```rb
# hash_symbol_2.rb
mark = Hash.new 0 # We specify default value of mark is zero
mark[:English] = 50
mark[:Math] = 70
mark[:Science] = 75
print "Enter subject name:"
sub = gets.chop.to_sym
puts "Mark in #{sub} is #{mark[sub]}"
```
Output
```rb
Enter subject name:Math
Mark in Math is 70
```
程序運行時,提示輸入主題名稱,輸入后顯示相應的標記。 讓我們遍歷代碼,看看它是如何工作的。 請注意,在標記哈希中,我們使用符號而不是字符串作為鍵,如圖所示
```rb
mark[:English] = 50
mark[:Math] = 70
mark[:Science] = 75
```
接下來,我們提示用戶使用打印“輸入主題名稱:”輸入標記,然后輸入主題名稱。 現在看下一行,首先我們使用`gets.chop`將主題名稱放入變量`sub`中。 `chop`會刪除你在按下鍵盤上的 Enter 鍵時輸入的輸入字符`\n`。 `to_sym`將你輸入的任何內容轉換為符號,所有這些最終存儲在`sub`中。
```rb
sub = gets.chop.to_sym
```
接下來,我們要做的就是訪問具有鍵`sub`的哈希值,并使用以下語句進行打印
```rb
puts "Mark in #{sub} is #{mark[sub]}"
```
因此,你已經看到了該程序 [hash_creation_1.rb](code/hash_creation_1.rb) ,我們現在已經了解符號,因此可以將其編寫如下:
```rb
#!/usr/bin/ruby
# hash_creation_1_a.rb
marks = { :English => 50, :Math => 70, :Science => 75 }
puts "Key => Value"
marks.each { |a,b|
puts "#{a} => #{b}"
}
```
Output
```rb
Key => Value
English => 50
Math => 70
Science => 75
```
參見`marks = { :English ? 50, :Math ? 70, :Science ? 75 }`行,我們在這里使用符號而不是字符串作為哈希鍵。 與字符串相比,哈希具有一些優勢,因為與字符串相比,它們占用的內存更少(在程序運行期間)。
在 ruby 1.9 及更高版本中,有更好的寫 [hash_creation_1_a.rb](code/hash_creation_1_a.rb) 的方法,你可以在下面提到的 [hash_creation_2.rb](code/hash_creation_2.rb) 中看到。 只需在下面的程序中查看此`marks = { English: 50, Math: 70, Science: 75 }`行
```rb
#!/usr/bin/ruby
# hash_creation_2.rb
marks = { English: 50, Math: 70, Science: 75 }
puts "Key => Value"
marks.each { |a,b|
puts "#{a} => #{b}"
}
```
該程序與上一個程序完全一樣,但是行`marks = { English: 50, Math: 70, Science: 75 }`被翻譯(假定已翻譯)為以下代碼`marks = { :English ? 50, :Math ? 70, :Science ? 75 }`,因此它是一種新的簡短形式,用于聲明以符號為鍵的哈希,這是在 ruby 中新引入的。 1.9
### 7.5。 字符串,凍結的字符串&符號,它們的存儲足跡
字符串占用大量內存,讓我們看一個例子,啟動你的 irb 并輸入以下代碼
```rb
>> c = "able was i ere i saw elba"
=> "able was i ere i saw elba"
>> d = "able was i ere i saw elba"
=> "able was i ere i saw elba"
>> c.object_id
=> 21472860
>> d.object_id
=> 21441620
```
在上面的示例中,我們看到了兩個變量`c`和`d`,它們都分配給相同的字符串“ able is ire ere elba”,但是如果我通過調用`c.object_id`和`d.object_id`看到對象 ID 是 ,兩者是不同的。 這意味著這兩個“可能是我看見了厄爾巴島”存儲在不同的位置。 它們被復制并復制。
這意味著,假設你在程序中的多個位置聲明了相同的字符串,并且所有字符串都將占用新的內存,因此這將導致計算機 RAM 上的大量負載(對于大型程序)。
現在讓我們看看如果我們使用一種稱為凍結字符串的新事物會發生什么。 在 irb 中輸入以下代碼
```rb
>> a = "able was i ere i saw elba".freeze
=> "able was i ere i saw elba"
>> b = "able was i ere i saw elba".freeze
=> "able was i ere i saw elba"
>> a.object_id
=> 21633340
>> b.object_id
=> 21633340
```
現在,在上面的示例中,我們在字符串上調用了一個名為`freeze`的方法。 現在,當我檢查`a`和`b'的對象 ID 時,兩者是相同的。 這意味著它們都指向同一字符串。 他們只是占據一個空間。 就是這樣 在前面的示例中,每次都會創建新的東西,但是當我們為變量分配凍結字符串時,它將檢查天氣是否已經(凍結)聲明了該字符串,如果是,則僅指向同一位置。
現在讓我們看看符號如何占據空間,以及它們是否一次又一次地復制。
```rb
>> e = :some_symbol
=> :some_symbol
>> f = :some_symbol
=> :some_symbol
>> e.object_id
=> 1097628
>> f.object_id
=> 1097628
```
因此,在上面的示例中,我們將`e`和`f`分配給符號`:some_symbol`,當我們檢查它們的對象 ID 時,它們都是相同的,或者它們都指向相同的位置。 這意味著,如果我們一次又一次地聲明符號,它們將不會占用額外的空間。 凍結的字符串和符號對于存儲很有用。 普通的字符串是不好的。
那我為什么要在哈希部分說這個呢? 假設你看到了這段代碼
```rb
>> person = {"name" => "frank"}
=> {"name"=>"frank"}
>> person2 = {"name" => "bob"}
=> {"name"=>"bob"}
and you have this one
>> person = {"name".freeze => "frank"}
=> {"name"=>"frank"}
>> person2 = {"name".freeze => "bob"}
=> {"name"=>"bob"}
```
試想一下哪一個將占用最少的內存? 現在考慮在數十行代碼中使用相同哈希結構的大型程序……。
### 7.6。 壓實哈希
就像可以壓縮數組一樣,此新功能從 Ruby 2.4.0 引入到哈希中,使程序員可以對其進行壓縮。 讓我們看一個 irb 或 pry 的例子
```rb
>> hash = {a: 1, b: nil, c: 2, d: nil, e: 3, f:nil}
=> {:a=>1, :b=>nil, :c=>2, :d=>nil, :e=>3, :f=>nil}
>> hash.compact
=> {:a=>1, :c=>2, :e=>3}
>> hash
=> {:a=>1, :b=>nil, :c=>2, :d=>nil, :e=>3, :f=>nil}
>> hash.compact!
=> {:a=>1, :c=>2, :e=>3}
>> hash
=> {:a=>1, :c=>2, :e=>3}
```
該解釋與關于數組壓縮的解釋非常相似。 請參考。
### 7.7。 轉換哈希值
假設你有一個 Hash 對象,并且想要轉換其值。 假設你有一個哈希,其值是數字,并且想要將其轉換為它們的平方,然后在該 Hash 實例上使用 transform_values 函數。 讓我們來看看代碼,看看它是如何工作的。 點火或撬動
首先定義一個哈希,如下所示
```rb
>> hash = {a: 1, b: 2, c: 3}
=> {:a=>1, :b=>2, :c=>3}
```
現在我們要轉換值,所以我們在哈希表上調用`transform_values`,如下所示
```rb
>> hash.transform_values{ |value| value * value }
=> {:a=>1, :b=>4, :c=>9}
```
如果你注意到,它的功能與在 Array 中收集非常相似,但適用于 Hash 中的值。
現在讓我們看看哈希是否已更改
```rb
>> hash
=> {:a=>1, :b=>2, :c=>3}
```
從上面的代碼片段可以看出,答案是否定的。 如果要更改在其上調用`transform_values`的 Hash 實例,請如下所示用一個爆炸聲來調用它。 它將更改調用它的哈希對象。
```rb
>> hash.transform_values!{ |value| value * value }
=> {:a=>1, :b=>4, :c=>9}
>> hash
=> {:a=>1, :b=>4, :c=>9}
```
### 7.8。 閱讀 rubydoc
再次,這本書無法解釋哈希中的所有內容。 要了解更多信息,請參考 rubydocs 以獲取哈希值 [https://ruby-doc.org/core-2.5.1/Hash.html](https://ruby-doc.org/core-2.5.1/Hash.html)
- 前言
- 紅寶石
- 先決條件
- 1.安裝 Ruby
- 2.在線資源
- 3.入門
- 4.比較與邏輯
- 5.循環
- 6.數組
- 7.哈希和符號
- 8.范圍
- 9.功能
- 10.可變范圍
- 11.類&對象
- 12.安全導航
- 13.打破大型程序
- 14.結構和 OpenStruct
- 15. Rdoc
- 16. Ruby 樣式指南
- 17.模塊和混入
- 18.日期和時間
- 19.文件
- 20. Proc,Lambda 和塊
- 21.多線程
- 22.異常處理
- 23.正則表達式
- 24.寶石
- 25.元編程
- 26.基準
- 27.測試驅動開發
- 28.觀察者模式
- 29.模板模式
- 30.工廠模式
- 31.裝飾圖案
- 32.適配器模式
- 33.單例模式
- 34.復合模式
- 35.建造者模式
- 36.策略模式
- 贊助商
- 捐
- 人們怎么說
- 版權
- 取得這本書