Ruby:检测Array中转折点的最优雅方法

时间:2015-08-25 11:46:10

标签: arrays ruby

我们采取以下数组:

[1, 4, 5, 3, 1, 4, 6, 5, 4]

它具有以下转折点(当上升变化为下降时,反之亦然):

  • 5(索引2)
  • 1(索引4)
  • 6(索引6)

使任务更加通用:

  • 有一个数组a = [a1, a2, ...]
  • 功能p(x,y) -> z,其中zComparable
  • 如何获得所有元素 i ∈a(0 i-1 ,a < sub> i )!= p(a i ,a i + 1

我想写一些像:

a.detect_edges{|prev, n| prev >= n} # => [[5,2], [1, 4], [6,6]]

使用各自的索引获得转折点的最优雅方法是什么?这是我的代码,从美学的角度来看,我不满意:

class Array
  def detect_edges(&blk)
    return nil if self.length < 2
    prev = blk.call(self[0], self[1])
    result = []
    self[0..-2].each_with_index do |elem, i|
      current = blk.call(elem, self[i+1])
      if current != prev
        result.push [elem, i]
      end
      prev = current
    end
    result
  end
end

4 个答案:

答案 0 :(得分:5)

[1, 4, 5, 3, 1, 4, 6, 5, 4]
.each_cons(3).with_index(1)
.reject{|(e1, e2, e3), i| (e1 <=> e2) == (e2 <=> e3)}
.map{|(e1, e2, e3), i| [e2, i]}
# => [[5, 2], [1, 4], [6, 6]]

答案 1 :(得分:4)

看看,没有map

a = [1, 4, 5, 3, 1, 4, 6, 5, 4]

a[1..-2].each.with_index(1).reject { |e,i| (a[i-1]<=>e) == e<=>a[i+1] }    
  #=> [[5, 2], [1, 4], [6, 6]]

答案 2 :(得分:2)

所以你基本上想要元素及其索引,其中元素是1索引范围内的局部最大值:

arr.each.with_index.select { |element, index| element == arr[index.pred..index.next].max }
  # => [[5, 2], [6, 6]]

注意,您必须处理第一个元素的情况或元素是否相等。

<小时/> 编辑:对于您的更新版本,您只需检查<=>的结果是否已更改。请注意,当元素相等时,您将再次检查案例:

arr.each.with_index.to_a.tap(&:pop).drop(1).select do |element, index| 
  (arr[index.pred] <=> element) != (element <=> arr[index.next])
end # => [[5, 2], [1, 4], [6, 6]]

答案 3 :(得分:2)

我没有理由得到更多的幻想:

class Array
  def detect_edges
    self.collect.with_index do |e, i|
      next if i == 0 || i >= size-1
      yield(self[i-1],e) != yield(e,self[i+1]) ? [e, i] : nil
    end.compact
  end
end

请注意,在修补Array时,应该使用优化。