Rubyの魔法の列挙可能なモジュール
Ruby Magicの別のエピソードの時間です!今回は、Rubyの最も魅力的な機能の1つを見ていきます。これは、 Array
などのRubyの列挙可能なクラスを操作するときに使用するメソッドのほとんどを提供します。 、ハッシュ
およびRange
。このプロセスでは、列挙可能なオブジェクトで何ができるか、列挙がどのように機能するか、単一のメソッドを実装してオブジェクトを列挙可能にする方法を学習します。
列挙可能
、 #each
およびEnumerator
列挙 オブジェクトをトラバースすることを指します。 Rubyでは、オブジェクトを列挙可能と呼びます。 アイテムのセットと、各アイテムをループする方法について説明している場合。
組み込みの列挙型は、列挙型
を含めることで列挙機能を取得します モジュール。#include?code>などのメソッドを提供します。 、
#count
、 #map
、 #select
および#uniq
、 とりわけ。配列とハッシュに関連付けられているメソッドのほとんどは、実際にはこれらのクラス自体には実装されておらず、含まれています。
注 : #count
などのいくつかのメソッド および#take
Array
で クラス、は Enumerable
の配列を使用する代わりに、配列用に特別に実装されています モジュール。これは通常、操作を高速化するために行われます。
列挙可能
モジュールは#each
という名前のメソッドに依存しています 、これは、含まれているすべてのクラスに実装する必要があります。配列上のブロックで呼び出されると、 #each
メソッドは、配列の各要素のブロックを実行します。
irb> [1,2,3].each { |i| puts "* #{i}" }
* 1
* 2
* 3
=> [1,2,3]
#each
と呼ぶと 配列のメソッドなし 要素ごとに実行するブロックを渡すと、 Enumerator
のインスタンスを受け取ります。 。
irb> [1,2,3].each
=> #<Enumerator: [1, 2, 3]:each>
列挙子
のインスタンス オブジェクトを反復処理する方法を説明します。列挙子はオブジェクトを手動で反復し、列挙をチェーンします。
irb> %w(dog cat mouse).each.with_index { |a, i| puts "#{a} is at position #{i}" }
dog is at position 0
cat is at position 1
mouse is at position 2
=> ["dog", "cat", "mouse"]
#with_index
メソッドは、変更された列挙子がどのように機能するかを示す良い例です。この例では、 #each
列挙子を返すために配列で呼び出されます。次に、 #with_index
配列の各要素にインデックスを追加して、各要素のインデックスを印刷できるようにするために呼び出されます。
オブジェクトを列挙可能にする
内部的には、 #max
のようなメソッド 、 #map
および#take
#each
に依存する 機能する方法。
def max
max = nil
each do |item|
if !max || item > max
max = item
end
end
max
end
内部的には、列挙可能
のメソッドにはCの実装がありますが、上記の例は #max
の方法を大まかに示しています。 動作します。 #each
を使用する すべての値をループして最高値を記憶すると、最大値が返されます。
def map(&block)
new_list = []
each do |item|
new_list << block.call(item)
end
new_list
end
#map
関数は、渡されたブロックを各アイテムで呼び出し、結果を新しいリストに入れて、すべての値をループした後に返します。
Enumerable
のすべてのメソッド以降 #each
を使用する ある程度の方法では、カスタムクラスを列挙可能にするための最初のステップは、 #each
を実装することです。 メソッド。
#each
の実装
#each
を実装する 関数とEnumerable
を含む クラス内のモジュール、それは列挙可能になり、 #min
のようなメソッドを受け取ります 、 #take
および#inject
無料です。
ほとんどの状況では、配列などの既存のオブジェクトにフォールバックして、 #each
を呼び出すことができます。 その方法については、最初から自分で作成する必要がある例を見てみましょう。この例では、 #each
を実装します リンクリスト 列挙可能にするため。
リンクリスト:配列のないリスト
リンクリストはデータ要素のコレクションであり、各要素は次の要素を指します。リストの各要素には、 headという名前の2つの値があります。 とテール 。ヘッドは要素の値を保持し、テールはリストの残りの部分へのリンクです。
[42, [12, [73, nil]]
3つの値(42、12、および73)を持つリンクリストの場合、最初の要素のヘッドは42であり、テールは2番目の要素へのリンクです。 2番目の要素の頭は12で、尾は3番目の要素を保持します。 3番目の要素の頭は73で、尾は nil
です。 、これはリストの終わりを示します。
Rubyでは、 @head
という名前の2つのインスタンス変数を保持するクラスを使用してリンクリストを作成できます。 および@tail
。
class LinkedList
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def <<(item)
LinkedList.new(item, self)
end
def inspect
[@head, @tail].inspect
end
end
#<<
メソッドは、リストに新しい値を追加するために使用されます。これは、渡された値を先頭、前のリストを末尾として新しいリストを返すことで機能します。
この例では、 #inspect
メソッドが追加されたので、リストを調べて、含まれている要素を確認できます。
irb> LinkedList.new(73) << 12 << 42
=> [42, [12, [73, nil]]]
リンクリストができたので、 #each
を実装しましょう。 その上に。 #each
関数はブロックを受け取り、オブジェクトの値ごとにそれを実行します。リンクリストに実装する場合、リストの @head
で渡されたブロックを呼び出すことにより、リストの再帰的な性質を利用できます。 、 #each
を呼び出します @tail
に 、存在する場合。
class LinkedList
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def <<(item)
LinkedList.new(item, self)
end
def inspect
[@head, @tail].inspect
end
def each(&block)
block.call(@head)
@tail.each(&block) if @tail
end
end
#each
を呼び出す場合 リンクリストのインスタンスでは、渡されたブロックを現在の @head
で呼び出します。 。次に、 @tail
のリンクリストでそれぞれを呼び出します テールがnil
でない限り 。
irb> list = LinkedList.new(73) << 12 << 42
=> [42, [12, [73, nil]]]
irb> list.each { |item| puts item }
42
12
73
=> nil
リンクリストが#each
に応答するようになりました 、 Enumberable
を含めることができます リストを列挙可能にするため。
class LinkedList
include Enumerable
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def <<(item)
LinkedList.new(item, self)
end
def inspect
[@head, @tail].inspect
end
def each(&block)
block.call(@head)
@tail.each(&block) if @tail
end
end
irb> list = LinkedList.new(73) << 12 << 42
=> [42, [12, [73, nil]]]
irb> list.count
=> 3
irb> list.max
=> 73
irb> list.map { |item| item * item }
=> [1764, 144, 5329]
irb> list.select(&:even?)
=> [42, 12]
列挙子
を返す インスタンス
リンクリスト内のすべての値をループできるようになりましたが、列挙可能な関数をチェーンすることはまだできません。そのためには、列挙子
を返す必要があります #each
のインスタンス 関数はブロックなしで呼び出されます。
class LinkedList
include Enumerable
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def <<(item)
LinkedList.new(item, self)
end
def inspect
[@head, @tail].inspect
end
def each(&block)
if block_given?
block.call(@head)
@tail.each(&block) if @tail
else
to_enum(:each)
end
end
end
オブジェクトを列挙型でラップするには、 #to_enum
を呼び出します。 その上でメソッド。 :each
を渡します 、それは列挙子が内部で使用する必要がある方法です。
次に、 #each
を呼び出します ブロックのないメソッドを使用すると、列挙を連鎖させることができます。
irb> list = LinkedList.new(73) << 12 << 42
=> [42, [12, [73, nil]]]
irb> list.each
=> #<Enumerator: [42, [12, [73, nil]]]:each>
irb> list.map.with_index.to_h
=> {42=>0, 12=>1, 73=>2}
9行のコードとインクルード
#each
を実装する Enumerable
を使用する モジュールと戻り値Enumerator
独自のオブジェクトから、9行のコードとインクルードを追加することでリンクリストを強化することができました。
これで、Rubyの列挙型の概要は終わりです。この記事についてのご意見、またはご不明な点がございましたら、お気軽にお問い合わせください。私たちは常に調査と説明を行うトピックを探しています。Rubyに何か魔法のようなものがあれば、遠慮なく@AppSignalまでお問い合わせください。
-
Ruby2.6の9つの新機能
Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,
-
Ruby Enumerable Moduleの基本ガイド(+私のお気に入りの方法)
列挙可能とは何ですか? 列挙可能は反復法のコレクションです 、Rubyモジュール、そしてRubyを優れたプログラミング言語にする大きな部分です。 列挙可能には次のような便利なメソッドが含まれます : マップ 選択 注入 列挙可能なメソッドは、ブロックを与えることで機能します。 そのブロックで、すべての要素で何をしたいのかを伝えます。 例 : [1,2,3].map { |n| n * 2 } すべての数値が2倍になった新しい配列を提供します。 正確に何が起こるかは、使用する方法、 mapによって異なります。 すべての値を変換するのに役立ちます、 select リス