【问题标题】:How to interpolate an array?如何插入一个数组?
【发布时间】:2011-12-22 15:41:56
【问题描述】:

我想用Array 做类似join 的事情,但我不想得到String 的结果,我想得到Array。我会称之为interpolate。例如,给定:

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

我希望:

a.interpolate(0) # => [1, 0, 2, 0, 3, 0, 4, 0, 5]  
a.interpolate{Array.new} # => [1, [], 2, [], 3, [], 4, [], 5]

获得这个的最佳方法是什么?我需要它来获取块的原因是因为当我将它与块一起使用时,我希望每个插值器都有不同的实例。


在得到很多人的好答案后,我想出了一些修改过的答案。

这是对 tokland 答案的修改。我让它接受nilconj1。并且还将if conj2 条件移到flat_map 循环之外以使其更快。

class Array
    def interpolate conj1 = nil, &conj2
        return [] if empty?
        if conj2 then first(length - 1).flat_map{|e| [e, conj2.call]}
        else          first(length - 1).flat_map{|e| [e, conj1]}
        end << last
    end
end

这是对 Victor Moroz 答案的修改。我添加了接受块的功能。

class Array
    def interpolate conj1 = nil, &conj2
        return [] if empty?
        first, *rest = self
        if conj2 then rest.inject([first]) {|a, e| a.push(conj2.call, e)}
        else          rest.inject([first]) {|a, e| a.push(conj1, e)}
        end
    end
end

基准测试后,第二个看起来更快。看来flat_map虽然看起来很漂亮,但速度很慢。

【问题讨论】:

  • 为什么用Array.new 屏蔽?你不能把它作为参数传递吗?
  • @Linux_iOS.rb.cpp.c.lisp.m.sh 也许我的例子不好,但有时,我需要为每个插值器使用不同的实例。
  • 这是有道理的。这意味着我的回答一文不值。对不起。

标签: ruby arrays ruby-1.9


【解决方案1】:

使用zip:

a.zip(Array.new(a.size) { 0 }).flatten(1)[0...-1]

【讨论】:

  • 在我的系统上,它产生的 [[1, 0], [2, 0], [3, 0]] 与期望的输出相差甚远:-)
  • 我猜你忘了.flatten(1)[0...-1],除了它创建了你实际上不需要的新数组
  • 你的想法看起来不错。我正在将您的答案与 Victor Moroz 的答案进行比较,并想知道哪个更快。
  • “想知道哪个更快”,别想,基准测试!但是,考虑到zip 是用 C 语言编写的,我会认为它更快。
  • "哪个更快" ...除非您有足够大的数据集并观察到问题,否则不要担心。保持代码可读性,如果出现速度下降,请使用分析工具和基准来追踪瓶颈。
【解决方案2】:

另一种方式

class Array
  def interpolate(pol=nil)
    new_ary = self.inject([]) do |memo, orig_item|
      pol = yield if block_given?
      memo += [orig_item, pol]
    end
    new_ary.pop
    new_ary
  end
end

[1,2,3].interpolate("A")
#=> [1, "A", 2, "A", 3]

[1,2,3].interpolate {Array.new}
#=> [1, [], 2, [], 3]

【讨论】:

  • 谢谢。我看到你的答案是优越的,因为它允许nil 用于pol。但我有点担心inject 可能很重。
  • 我做了基准测试,看来+= 很重。 inject 不是。
【解决方案3】:
class Array
  def interpolate_with val
    res = []
    self.each_with_index do |el, idx|
      res << val unless idx == 0
      res << el
    end
    res
  end
end

用法:

ruby-1.9.3-p0 :021 > [1,2,3].interpolate_with 0
 => [1, 0, 2, 0, 3] 
ruby-1.9.3-p0 :022 > [1,2,3].interpolate_with []
 => [1, [], 2, [], 3] 

【讨论】:

  • 这看起来不错。我正在将此与其他答案进行比较。
【解决方案4】:

不太确定你想用块做什么,但我会这样做:

class Array
  def interpolate(sep)
    h, *t = self
    t.empty? ? [h] : t.inject([h]) { |a, e| a.push(sep, e) }
  end
end

更新:

基准(数组大小 = 100):

            user     system      total        real
inject  0.730000   0.000000   0.730000 (  0.767565)
zip     1.030000   0.000000   1.030000 (  1.034664)

其实我有点意外,我以为zip会更快。

更新 2:

zip 更快,flatten 不是。

【讨论】:

  • 我喜欢你关于h, *t = self 的想法,但inject 不是很重吗?我想知道您的答案和创建数组之间哪个更快,zipping 就像 lucapette 的答案一样。
  • 看来你的回答很快。感谢您的基准测试。
【解决方案5】:

这是一个使用flat_mapeach_cons 的简单版本(可以处理多个值和/或一个块):

class Array
  def interpolate *values
    each_cons(2).flat_map do |e, _|
      [e, *values, *(block_given? ? yield(e) : [])]
    end << last
  end
end

[1,2,3].interpolate(0, "") # => [1, 0, "", 2, 0, "", 3]
[1,2,3].interpolate(&:even?) # => [1, false, 2, true, 3]

【讨论】:

    【解决方案6】:

    这样做就地:

    class Array
      def interpolate(t = nil)
        each_with_index do |e, i|
          t = yield if block_given?
          insert(i, t) if i % 2 == 1
        end
      end
    end
    

    之所以有效,是因为t被插入到了当前索引的元素之前,使得刚刚插入的t成为了当前索引的元素,也就意味着迭代可以正常继续。

    【讨论】:

      【解决方案7】:

      有很多方法可以做到这一点。例如(Ruby 1.9):

      class Array
        def intersperse(item = nil)
          return clone if self.empty?
          take(self.length - 1).flat_map do |x|
            [x, item || yield]
          end + [self.last]
        end
      end
      
      p [].intersperse(0)
      #=> []
      
      p [1, 2, 3, 4, 5].intersperse(0)
      #= >[1, 0, 2, 0, 3, 0, 4, 0, 5]
      
      p [1, 2, 3, 4, 5].intersperse { 0 }
      #= >[1, 0, 2, 0, 3, 0, 4, 0, 5]
      

      (我使用 Haskell 函数名:intersperse。)

      【讨论】:

      • 谢谢。这与我想要的非常接近。
      • 我认为您可以将return 条件放松为if self.empty?
      • @sawa,编辑某人的答案被认为是不好的形式。您可以编辑您的问题以附加您的版本,也可以向他们的问题添加评论。答案的编辑应该是拼写、格式的可读性等,但代码应该是神圣不可侵犯的。
      • @sawa:确实,使用“empty?”进行了编辑。我真的不介意你编辑了这个问题,但是铁皮人是对的,最好创建一个单独的答案。
      • 我喜欢美丽或你的代码,但经过密集的基准测试,Victor Moroz 的修改以允许 proc 的答案似乎是最快的。
      【解决方案8】:

      这是一种方法:

      theArray.map {|element| [element, interpolated_obj]}.flatten
      

      【讨论】:

      • 您将有 interpolated_obj 作为结果的最后一个元素,这看起来不像是期望的行为。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-28
      • 1970-01-01
      • 1970-01-01
      • 2014-04-04
      • 2022-01-13
      • 2015-09-19
      相关资源
      最近更新 更多