Ruby中的一阶数组差分

Ruby中的一阶数组差分,ruby,Ruby,最光滑、最像红宝石的方法是什么 [1, 3, 10, 5].diff 应该产生 [2, 7, -5] 也就是说,一组一阶差分。我已经提出了一个解决方案,我将在下面添加它,但它需要Ruby1.9,而且不太灵巧。还有什么可能呢?这个概念当然来自函数式编程: # Attempt, requires ruby 1.9. module Enumerable def diff each_cons(2).with_object([]){|x,array| array << x[1]

最光滑、最像红宝石的方法是什么

[1, 3, 10, 5].diff
应该产生

[2, 7, -5]

也就是说,一组一阶差分。我已经提出了一个解决方案,我将在下面添加它,但它需要Ruby1.9,而且不太灵巧。还有什么可能呢?

这个概念当然来自函数式编程:

# Attempt, requires ruby 1.9.
module Enumerable
  def diff
    each_cons(2).with_object([]){|x,array| array << x[1] - x[0]}
  end
end
module Enumerable
  def diff
    self.inject([0]) { |r,x| r[-1] += x; r << -x } [1..-2]
  end
end

[1,3,10,5].diff
模块可枚举
def diff
self.inject([0]){| r,x | r[-1]+=x;r另一种方法

module Enumerable
  def diff
    result = []
    each_with_index{ |x, i|
      return result if (i == (self.length-1))
      result << self[i+1] - x
    }
  end
end
模块可枚举
def diff
结果=[]
每个_都有_索引{x,i|
如果(i==(self.length-1))返回结果

结果我喜欢这种功能性风格:

module Enumerable
  def diff
    each_cons(2).map {|pair| pair.reverse.reduce :-}
  end
end
编辑:我刚刚意识到,
reverse
是完全没有必要的。如果这是一种函数式语言,我会使用模式匹配,但Ruby不支持模式匹配。不过,它支持解构绑定,这对于这种情况下的模式匹配来说是一个足够好的近似

each_cons(2).map {|first, second| second - first}
不过,没有笑脸

如果你只是从左到右大声读出来,我喜欢这样的声音:“对于每一对,应用第一个和第二个元素之间的差异。”事实上,我通常不喜欢名称
collect
,而更喜欢
map
,但在这种情况下读起来更好:

each_cons(2).collect {|first, second| second - first}

“对于每一对,收集其元素之间的差异。”听起来几乎像是一阶差异的定义。

另一种方式……似乎是迄今为止最短的:)

我无力的尝试

module Enumerable
  def diff
    na = []
    self.each_index { |x| r << self[x]-self[x-1] if x > 0 }
    na
  end
end

p [1,3,10,5].diff  #returned [2, 7, -5]
模块可枚举
def diff
na=[]
self.each|u索引{| x | r0}
na
结束
结束
p[1,3,10,5].diff#返回[2,7,-5]

以下是我能找到的最快的方法(比目前为止在1.8和1.9中建议的所有其他方法都要快):

在这个紧随其后的亚军中:

module Enumerable
  def diff
    r = []
    1.upto(size-1) {|i| r << self[i]-self[i-1]}
    r
  end
end

但这(出于我不完全理解的原因)比这里的任何其他建议都慢了一个数量级!

Jörg W Mittag的微小变化:

module Enumerable
  def diff
    each_cons(2).map{|a,b| b-a}
  end
end

使用
inject
来构建列表怎么样?将方法从3行缩短为1行,并且更易于阅读(当然是imo,但它至少更像ruby).实际上只慢了2.6倍,但仍然如此。哦,在长数组上,差异更大。对于1000个数组,这比我最快的版本慢了10倍左右!但是这个解决方案有一个内置的笑脸!很好,这是最清晰的(如果1.9可以)在我的测试中比我的版本慢了0.1.6倍…哇,我不知道我的解决方案快。我只是尝试用我熟悉的几种方法使它工作(对不起,还是个新手)。喜欢这个线程,我了解了每种方法和注入:)如果执行速度是主要关注点,那么您可能不会从一开始就使用Ruby,但我认为运行测试总是很有趣的。大多数现有Ruby实现在方法调用方面非常糟糕。此外,广泛使用的实现在对象调用方面也非常糟糕分配。在您的解决方案中,您对数组的每个元素递归一次,并且每一步分配两个新数组,因此总的来说,您的调用堆栈深度为n,每一步有6个方法调用(总共6n个),总共分配了2n个数组。所有这些在当前广泛使用的VM上都非常缓慢。新的(尚未发布)JRuby JIT编译器具有数组分配消除功能(以及其他功能),并且可能会显著加快这一速度。特别是对于包含数十万个元素的大型数组,当方法被频繁调用时,JVM的本机代码JIT编译器会起作用(在
java-server
模式下,这是最快的,编译器的默认阈值大约是10000次调用,甚至在它开始编译之前)。哦,我忘了,你在那里有异常处理,大多数当前的虚拟机在这方面也很糟糕。没有理由不把你的代码编译成超高效的机器代码(事实上,如果我们谈论的是一个商业的Smalltalk或Lisp实现,或者Supero Haskell编译器,它可能会。Ruby实现并没有那么好。然而!好消息是,这只是一个金钱、努力、人力和博士论文的问题。在Ruby上花的钱和我们在Java上花的钱一样多,Ruby轻而易举地击败了C。)。坏消息是:这些都不多。)您可以在块中使用解构绑定而不是索引:
{first,second{first-second}
。此外,在1.8.7上,
self[1..-1]
相当于
drop(1)
,使整个内容如下所示:
drop(1).zip(self).collect{| first,second | first-second}
。同样,即使你不懂Ruby,只是像英语一样大声读出来,它听起来几乎是可以理解的,就像一阶差分的数学定义:“通过计算差分并收集结果,将数组与自身移位1的数组结合起来。”谢谢,顺便说一句。我昨天在寻找这样的解决方案,结果大脑冻结了。虽然我自己应该能够想出这个解决方案,但毕竟它与我最喜欢的Haskell代码密切相关:
fibs=0:1:zipWith(+)fibs(tail fibs)
module Enumerable
  def diff
    last=nil
    map do |x|
      r = last ? x - last : nil
      last = x
      r
    end.compact
  end
end
module Enumerable
  def diff
    r = []
    1.upto(size-1) {|i| r << self[i]-self[i-1]}
    r
  end
end
module Enumerable
  def diff!
    [-shift+first] + diff! rescue []
  end

  def diff
    dup.diff!
  end
end
module Enumerable
  def diff
    each_cons(2).map{|a,b| b-a}
  end
end