我遇到了关于creating memory-efficient Ruby pipe class with lazy evaluation.的问题。有些代码用于使用惰性枚举器有效地创建命令管道。我一直在搞乱它,并实现了如下所示的tee
命令。我觉得有更好的方法可以做到这一点。
class Pipe
class Tee
def initialize(pipe)
@pipe = pipe
@buffer = []
@pipe.source = self.to_enum
end
def <<(item)
@buffer << item
# puts "Added '#{item}' to buffer: #{@buffer.inspect}"
end
def to_enum
Enumerator.new do |yielder|
item = @buffer.shift
yielder << item if item
end.lazy
end
end
attr_writer :source
def initialize
@commands = []
@source = nil
end
def add(command, opts = {})
@commands << [command, opts]
self
end
def run
enum = @source
@commands.each do |command, options|
enum = method(command).call(enum, options)
end
enum.each {}
enum
end
def cat(enum, options)
Enumerator.new do |yielder|
enum.map { |line| yielder << line } if enum
options[:input].tap do |ios|
ios.each { |line| yielder << line }
end
end.lazy
end
def out(enum, options)
Enumerator.new do |yielder|
enum.each do |line|
puts line
yielder << line
end
end.lazy
end
def cut(enum, options)
Enumerator.new do |yielder|
enum.each do |line|
fields = line.chomp.split(%r{#{options[:delimiter]}})
yielder << fields[options[:field]]
end
end.lazy
end
def upcase(enum, options)
Enumerator.new do |yielder|
enum.each do |line|
yielder << line.upcase
end
end
end
def tee(enum, options)
teed = Tee.new(options.fetch(:other))
Enumerator.new do |yielder|
enum.each do |line|
yielder << line
teed << line
end
end
end
def grep(enum, options)
Enumerator.new do |yielder|
enum.each do |line|
yielder << line if line.match(options[:pattern])
end
end.lazy
end
end
another_pipe = Pipe.new
another_pipe.add(:grep, pattern: /testing/i)
another_pipe.add(:out)
pipe = Pipe.new
pipe.add(:cat, :input => StringIO.new("testing\na new\nline"))
# pipe.add(:grep, pattern: /line/)
pipe.add(:tee, :other => another_pipe)
pipe.add(:upcase)
pipe.add(:out)
pipe.run
puts "================================================="
another_pipe.run
这是输出:
TESTING
A NEW
LINE
=================================================
testing
有没有人看到更聪明的方法呢?