2009-02-24 4 views
8

Resumen: La pregunta básica aquí fue, descubrí, si puede pasar un bloque de código a una matriz de Ruby que realmente reducirá el contenido de esa matriz a otra array, no a un solo valor (la forma en que inyecta lo hace). La respuesta corta es no".Corte una matriz de Ruby según las rayas dentro de ella

Estoy aceptando la respuesta que dice esto. Gracias a Squeegy por una excelente estrategia de bucle para obtener rayas de una matriz.

The Challenge: Para reducir los elementos de una matriz sin pasar por ella explícitamente.
la entrada: Todos los números enteros de -10 a 10 (excepto 0) ordenó al azar.
La salida deseada: Una matriz que representa rayas de números positivos o negativos. Por ejemplo, un -3 representa tres números negativos consecutivos. A 2 representa dos números positivos consecutivos.

Script de ejemplo:

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} 

salidas de la muestra:

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 

Nota unas pocas cosas:

  • La matriz de rayas tiene valores positivos y negativos alterna.
  • La suma de las rayas de los elementos es siempre 0 (como la suma del original).
  • La suma de los valores absolutos de la matriz racha es siempre 20.

esperanza que está claro!

Edit: ¡Me doy cuenta de que construcciones como rechazo! en realidad están recorriendo el conjunto en el fondo. No excluyo el bucle porque soy una persona mala. Solo estoy buscando aprender sobre el idioma. Si la iteración explícita es necesaria, está bien.

+0

¿Por qué no iterar a través de la matriz? ¿Cómo espera procesar los contenidos de una matriz sin recorrerla? Incluso si usa un método para hacer esto, seguirá recorriendo la matriz internamente. –

+0

te das cuenta de que cada método que llamaste en el código de tu ejemplo estaba de hecho recorriendo la matriz ... – kgrad

+0

Sí, exactamente, no hay forma de reducir una matriz sin recorrerla ... no habría forma de acceder el contenido. – cdmckay

Respuesta

1

Desde Ruby 1.9 hay una manera mucho más sencilla de resolver este problema:

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

Enumerable.chunk agrupará todos los elementos consecutivos de una matriz juntos por la salida de un bloque:

>> 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]]] 

Esto es casi exactamente lo que OP solicita, excepto que los grupos resultantes deben contar para obtener la matriz de rayas finales.

+0

¡Gracias! Esto es lo que estaba buscando originalmente. –

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 

La magia aquí es el operador XOR ^.

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

Así que si el último número de la matriz está en el mismo lado de cero a medida que el número está procesando, y luego añadirlo a la racha, de lo contrario añadir a la matriz de rayas para iniciar una nueva racha. Tenga en cuenta que sine true^true devuelve false tenemos que negar toda la expresión.

+0

Pero ... ¡estás haciendo bucles! –

+0

Esto no me parece correcto. ¿Estás contando o sumando? – jcrossley3

+0

Vaya, estoy un poco mal conectado. Esto suma los valores, en la racha no los cuenta. Tienes razón. Lo arreglaré. –

6
new_array = original_array.dup 
<Squeegy's answer, using new_array> 

Ta da! Sin bucles a través de la matriz original. Aunque dentro de dup es un MEMCPY, que supongo que se podría considerar un bucle en el nivel de ensamblador?

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

EDIT:;)

+0

+1 para la comedia, otros han conseguido muchos puntos para peor ... –

11

Bueno, aquí está una versión de una línea, si eso te satisface más:

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

Y si incluso inyectar es demasiado descabellado para usted , aquí hay una manera muy tonta:

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

Pero yo creo que está bastante claro que es mejor con algo mucho más sencillo:

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

Además de ser mucho más fácil de entender y mantener, la versión de "bucle" se ejecuta en aproximadamente dos tercios del tiempo de la versión inyectada, y aproximadamente una sexta parte del tiempo eval/regexp.

PD: Esta es una versión más potencialmente interesantes:

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} 

Este utiliza dos pasos, primero la construcción de una matriz de matrices racha, a continuación, convertir la matriz de matrices a una gran variedad de tamaños firmados. En Ruby 1.8.5, esto es en realidad un poco más rápido que la versión de inyección anterior (aunque en Ruby 1.9 es un poco más lento), pero el bucle aburrido sigue siendo el más rápido.

+0

Podría usar un comentario o dos. Entiendo que lo hayas explicado, pero poner comentarios en el código es algo que a menudo se descuida. –

1

Más abuso cadena, al estilo de Glenn McDonald, sólo es diferente:

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] 
Cuestiones relacionadas