⛳
[Feature #20625] ツリーやリスト構造のデータを走査する Object#chain_of メソッドを追加する提案
[Feature #20625] Object#chain_of
- 次のように要素が連鎖しているツリーやリストのような構造をたどるようなことはある
# ファイルシステムの構造を指定してファイルやディレクトリを取得する
def breadcrumbs(root)
crumbs = []
current = root
while current
crumbs << current
current = current.parent_dir
end
crumbs
end
- このような走査を行う
Object#chain_of
メソッドを追加する提案
class Object
def chain_of(&block)
chain = []
current = self
while current
chain << current
current = block.call(current)
end
chain
end
end
- これを利用すると以下のようなリンク構造を走査することができる
class ListNode
attr_accessor :value, :parent
def initialize(value, parent = nil)
@value = value
@parent = parent
end
def ancestors
chain_of(&:parent).tap(&:shift)
end
end
root = ListNode.new("root")
child1 = ListNode.new("child1", root)
child2 = ListNode.new("child2", child1)
# pp child2.ancestors.map(&:value)
# => ["child1", "root"]
- 要は『ブロックが次の要素を返して、それが
nil
になるまで走査を繰り返す』みたいな挙動になる感じですね- 今回でいうと『
#parent
がnil
になるまで繰り返す』みたいな挙動になる
- 今回でいうと『
- この挙動に近いのが
Enumerator.produce
でこっちは『ブロックを呼び出し続ける』みたいな挙動になる感じですね - 個人的には便利なケースもありそうだなあ、と思いつつ
Object
に直接生やしちゃうのはやや過剰な気がする - ちなみに
Enumerator.produce
を利用すると以下のように記述することはできます
pp Enumerator.produce(child2) { _1.parent or raise StopIteration }.map(&:value)
# => ["child2", "child1", "root"]
-
raise StopIteration
の部分がやや冗長なので以下のようにEnumerator
にメソッドを追加してもいいのかも
class Enumerator
def self.chain_of(initial = nil, &block)
produce(initial) { block.call(_1) or raise StopIteration }
end
end
pp Enumerator.chain_of(child2, &:parent).map(&:value)
# => ["child2", "child1", "root"]
- 個人的には
Enumerator
に定義する方が好み
Discussion