Как интерполировать массив? - PullRequest
0 голосов
/ 22 декабря 2011

Я хотел бы сделать что-то вроде join с Array, но вместо того, чтобы получить результат как 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]

Каков наилучший способ получить это? Причина, по которой мне нужен блок, состоит в том, что когда я использую его с блоком, я хочу разные экземпляры для каждого интерполятора, который находится между ними.


Получив отличные ответы от многих, я придумал несколько модифицированных.

Это модификация ответа токланда. Я заставил это принять nil для conj1. А также переместил условие 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

Это модификация ответа Виктора Мороза. Я добавил функциональность, чтобы принять блок.

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, хотя выглядит красиво, медленно.

Ответы [ 8 ]

6 голосов
/ 22 декабря 2011

Использование zip :

a.zip(Array.new(a.size) { 0 }).flatten(1)[0...-1]
3 голосов
/ 22 декабря 2011

Другой способ

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]
2 голосов
/ 22 декабря 2011
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] 
1 голос
/ 22 декабря 2011

Это делает это на месте:

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 элемент с текущим индексом, что означаетчто итерация может продолжаться в обычном режиме.

1 голос
/ 22 декабря 2011

Вот простая версия (которая может обрабатывать несколько значений и / или блок) с использованием flat_map и each_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]
1 голос
/ 22 декабря 2011

Так много способов сделать это. Например (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: вкрапления .)

1 голос
/ 22 декабря 2011

Не совсем уверен, что вы хотите сделать с блоком, но я бы сделал это так:

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

UPDATE:

Тесты (размер массива = 100):

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

На самом деле я немного удивлен, я думал, что zip будет быстрее.

UPDATE2:

zip быстрее, flatten нет.

0 голосов
/ 22 декабря 2011

Вот один из способов:

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