在 Elixir 中,有一个很棒的管道操作符,其工作方式如下:
"hello, world!"
|> String.split(" ")
|> Enum.map(&String.capitalize/1)
|> Enum.join
在 Ruby 中我们可以使用类似的语法:
"hello, world!"
.split(" ")
.map(&:capitalize)
.join
仅当为对象本身定义了所有这些方法时,它才有效。如果需要调用一些本地方法,我们应该使用类似:
.map { |el| URI.parse(el) }
但是如果我们想要进行一些集合处理(不是单个元素),例如GZIP压缩:
chars = text
.downcase
.chars
compressed = GZipped.new(chars).bytes
但是链条断了!
我找到了一些链接,但看起来不太好:
在我看来,有这样的东西会很棒:
text
.split
.pipe(URI.method(:parse))
.map(&:to_s)
.join
.pipe(GZIPped)
.pipe(Base64.method(:encode))
在 Ruby 中构建此类管道的最佳方法是什么?
这是一个例子
class Dedup
def initialize(obj)
@obj = obj
end
def each
Enumerator.new do |y|
prev = nil
@obj.each do |el|
if el != prev
y << el
prev = el
end
end
end
end
end
expect(
"1 1 1 2 2 3"
.split
.then { |obj| Dedup.new(obj).each }
.to_a
).to eq [1, 2, 3]
这种链接看起来丑陋且难以阅读。
比较:
expect(
"1 1 1 2 2 3"
.split
.pipe(Dedup)
.to_a
).to eq [1, 2, 3]
已经有这样的方法了,至少从 Ruby 2.5 开始 -
yield_self
,在 Ruby 2.6 中别名为 then
。您可以对任何响应 &
的对象使用 to_proc
运算符来传递它而不是块。
text
.split
.map(&URI.method(:parse)) # URI#parse expects a string, not an array
.map(&:to_s)
.join
.then(&GZIPped) # not sure what GZIPped is - I'll assume it has .to_proc method
.then(&Base64.method(:encode))
(我可能应该提到上面的代码实际上不会工作,老实说我不知道它会做什么 - 为什么要分割一个字符串,将它们转换为网址,然后再返回字符串?唯一的事情就是to 是要提高 id 其中一个子字符串不是有效的字符串?但是然后您尝试将结果字符串作为 gzip 压缩文件读取...我假设我误解了代码中的某些内容)
更高级的东西 - 我在 Elixir 中非常喜欢的一件事是将方法与剩余参数链接在一起的选项。这也可以在 ruby 中模拟,但需要一些工作并好好思考是否值得:
module MyMath
module_function
UNDEFINED = Object.new
def add(a, b = UNDEFINED)
if b == UNDEFINED
return ->(num) { add(a, num) }
end
a + b
end
end
MyMath.add(2,5) #=> 7
[1,2,5,9].map(&MyMath.add(5)] #=> [6,7,10,14]
我不知道这对你的情况是否有帮助,但是 Greg Navis 发表了“9 Lines of Ruby 中的 Elixir-style Pipelines”,这肯定与这个普遍问题相关。