根据其中的条纹将Ruby数组块化

时间:2009-02-24 17:10:17

标签: ruby

摘要:我发现,这里的基本问题是,您是否可以将代码块传递给Ruby数组,这实际上会将该数组的内容减少到另一个数组,而不是单个值(注入的方式)。最简洁的答案是不”。

我接受了这样说的答案。感谢Squeegy提供了一个很好的循环策略来从阵列中获取条纹。

挑战:减少数组元素而不显式循环显示 输入:从-10到10的所有整数(0除外)随机排序 所需输出:表示正数或负数条纹的数组。例如,-3表示三个连续的负数。 A 2代表两个连续的正数。

示例脚本:

original_array = (-10..10).to_a.sort{rand(3)-1}
original_array.reject!{|i| i == 0} # remove zero

streaks = (-1..1).to_a # this is a placeholder.  
# The streaks array will contain the output.
# Your code goes here, hopefully without looping through the array

puts "Original Array:"
puts original_array.join(",")
puts "Streaks:"
puts streaks.join(",")
puts "Streaks Sum:"
puts streaks.inject{|sum,n| sum + n}

示例输出:

Original Array:
3,-4,-6,1,-10,-5,7,-8,9,-3,-7,8,10,4,2,5,-2,6,-1,-9
Streaks:
1,-2,1,-2,1,-1,1,-2,5,-1,1,-2
Streaks Sum:
0


Original Array:
-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10
Streaks:
-10,10
Streaks Sum:
0

请注意以下几点:

  • 条纹阵列具有交替的正值和负值。
  • 元素条纹数组的总和始终为0(原始的总和)。
  • 条纹数组的绝对值之和始终为20.

希望这很清楚!

编辑:我确实意识到这样的构造就像拒绝一样!实际上是在后台循环遍历数组。我不排除循环,因为我是一个卑鄙的人。只是想了解这门语言。如果需要显式迭代,那很好。

5 个答案:

答案 0 :(得分:11)

嗯,这是一个单行版本,如果能让你满意的话:

streaks = original_array.inject([]) {|a,x| (a.empty? || x * a[-1] < 0 ? a << 0 : a)[-1] += x <=> 0; a}

如果连注入对你来说太环,那么这是一种非常愚蠢的方式:

  streaks = eval "[#{original_array.join(",").gsub(/((\-\d+,?)+|(\d+,?)+)/) {($1[0..0] == "-" ? "-" : "") + $1.split(/,/).size.to_s + ","}}]"

但我认为很明显你会更好地做一些更直接的事情:

streaks = []
original_array.each do |x|
  xsign = (x <=> 0)
  if streaks.empty? || x * streaks[-1] < 0
    streaks << xsign
  else
    streaks[-1] += xsign
  end
end

除了更易于理解和维护之外,“循环”版本在注入版本的大约三分之二时间内运行,并且在eval / regexp版本的大约六分之一时间内运行。

PS:这是另一个可能有趣的版本:

a = [[]]
original_array.each do |x|
  a << [] if x * (a[-1][-1] || 0) < 0
  a[-1] << x
end
streaks = a.map {|aa| (aa.first <=> 0) * aa.size}

这使用两次传递,首先构建一个条纹数组数组,然后将数组数组转换为有符号大小的数组。在Ruby 1.8.5中,这实际上比上面的注入版本稍快一些(虽然在Ruby 1.9中它稍微慢一些),但是无聊的循环仍然是最快的。

答案 1 :(得分:6)

new_array = original_array.dup
<Squeegy's answer, using new_array>
塔达!没有循环遍历原始数组。虽然里面是dup,但它是一个MEMCPY,我想它可能被认为是汇编程序级别的循环?

http://www.ruby-doc.org/doxygen/1.8.4/array_8c-source.html

编辑:;)

答案 2 :(得分:4)

original_array.each do |num|
  if streaks.size == 0
    streaks << num
  else
    if !((streaks[-1] > 0) ^ (num > 0))
      streaks[-1] += 1
    else
      streaks << (num > 0 ? 1 : -1)
    end
  end
end

这里的魔力是^ xor运算符。

true ^ false  #=> true
true ^ true   #=> false
false ^ false #=> false

因此,如果数组中的最后一个数字与正在处理的数字位于零的同一侧,则将其添加到条纹中,否则将其添加到条纹数组以开始新的条纹。请注意,正弦true ^ true返回false我们必须否定整个表达式。

答案 3 :(得分:1)

更多的字符串滥用,一个格伦麦当劳,只是不同:

runs = original_array.map do |e|
  if e < 0
    '-'
  else
    '+'
  end
end.join.scan(/-+|\++/).map do |t|
  "#{t[0..0]}#{t.length}".to_i
end

p original_array
p runs
# => [2, 6, -4, 9, -8, -3, 1, 10, 5, -7, -1, 8, 7, -2, 4, 3, -5, -9, -10, -6]
# => [2, -1, 1, -2, 3, -2, 2, -1, 2, -4]

答案 4 :(得分:1)

从Ruby 1.9开始,有一种更简单的方法可以解决这个问题:

original_array.chunk{|x| x <=> 0 }.map{|a,b| a * b.size }

Enumerable.chunk将通过块的输出将数组的所有连续元素组合在一起:

>> original_array.chunk{|x| x <=> 0 }
=> [[1, [3]], [-1, [-4, -6]], [1, [1]], [-1, [-10, -5]], [1, [7]], [-1, [-8]], [1, [9]], [-1, [-3, -7]], [1, [8, 10, 4, 2, 5]], [-1, [-2]], [1, [6]], [-1, [-1, -9]]]

这几乎就是OP所要求的,除了需要计算得到的组以获得最终的条纹数组。