1

我想.each在数组上扩展方法以实现如下语法:

arr = ["a","b","c","d"]
arr.each do |el| puts i unless el.last? end

显然我可以这样做:

arr = ["a","b","c","d"]
arr.each_with_index do |i, index| puts i unless index+1 == arr.length end

但我喜欢把这个逻辑抽象成一个last?方法。

我该怎么做?

4

2 回答 2

1

扩展正在生成的对象是错误的做法,因为对象本身不应该知道它包含在给定的集合中(如果您在多个数组中有相同的对象怎么办?)

如果您只想避免对数组中的最后一项进行操作,为什么不这样做:

arr[0..-2].each {|elem| ... }

您还可以使用 Darshan 的第二个答案的变体扩展 Enumerable,允许您排除任何给定可枚举中的最后一个元素:

module Enumerable
  def except_last
    each_with_index do |el, index|
      yield el unless index == count - 1
    end
  end
end

[1,2,3,4,5].each.except_last {|e| print e }
1234

(在这种情况下,each实际上是多余的,但它很好并且可读。)

于 2013-06-19T03:17:32.000 回答
1

这非常接近:

def each_with_last(arr)
  arr.each_with_index do |el, index|
    yield el, index + 1 == arr.length
  end
end

arr = ["a","b","c","d"]
each_with_last(arr) {|el, last| puts el unless last}

正如 Dave Newton 指出的那样,您可以向正在生成的对象添加一个last?方法,而不是生成一个额外的布尔值,但是,正如 icktoofay 指出的那样,这可能会变得混乱。

如果您的实际用例涉及忽略最后一个值,就像您在这里所做的那样,我认为这更干净:

def all_but_last(arr)
  arr.each_with_index do |el, index|
    yield el unless index + 1 == arr.length
  end
end

arr = ["a","b","c","d"]
all_but_last(arr) {|i| puts i}

更新:为了完整起见,虽然我不建议这样做:

module Enumerable
  def with_last?
    each_with_index do |el, index|
      flag = index + 1 == count
      el.define_singleton_method(:last?) {flag}
      yield el
    end
  end
end

arr = ["a","b","c","d"]
arr.each.with_last? {|el| puts el unless el.last?}

如果arr包含无法在其上定义单例方法的对象(例如 Symbols 和 Fixnums),那将不起作用。

当然,您可以对each自己执行此操作以使您的示例代码按给定的方式工作(除了el/i错字),但这会将事情从“推荐反对”升级为“真正糟糕的想法”。

于 2013-06-19T02:19:14.630 回答