2009-02-24 6 views
8

Sommario: La domanda di base qui era, ho scoperto, se è possibile passare un blocco di codice a un array di Ruby che effettivamente ridurrà il contenuto di tale array in un altro array, non su un singolo valore (il modo in cui si applica inietta). La risposta breve è "no".Chunk a matrice di Ruby in base a strisce all'interno di esso

Sto accettando la risposta che dice questo. Grazie a Squeegy per una grande strategia di looping per ottenere strisce da un array.

La sfida: Per ridurre gli elementi di un array senza eseguire il looping in modo esplicito.
Ingresso: Tutti i numeri interi da -10 a 10 (tranne 0) ordinati in ordine casuale.
L'output desiderato: Un array che rappresenta strisce di numeri positivi o negativi. Ad esempio, a -3 rappresenta tre numeri negativi consecutivi. A 2 rappresenta due numeri positivi consecutivi.

Script di esempio:

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} 

uscite Esempi:

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 un paio di cose:

  • L'array striature ha alternato valori positivi e negativi.
  • La somma della matrice di elementi streaks è sempre 0 (come la somma dell'originale).
  • La somma dei valori assoluti della matrice striscia è sempre 20.

Speranza che è chiaro!

Modifica: Mi rendo conto che tali costrutti come rifiutano! sono in realtà in loop attraverso la matrice in background. Non escludo il looping perché sono una persona cattiva. Sto solo cercando di conoscere la lingua. Se è necessaria un'iterazione esplicita, va bene.

+0

Perché non ripetere l'array? Come ti aspetti di elaborare il contenuto di un array senza doverlo scorrere. Anche se si utilizza un metodo per farlo, continuerà a eseguire il looping interno della matrice. –

+0

ti rendi conto che ogni metodo che hai chiamato nell'esempio di codice stava infatti eseguendo il ciclo attraverso l'array ... – kgrad

+0

Sì, esattamente, non c'è modo di ridurre un array senza doverlo scorrere ... non ci sarebbe modo di accedere il contenuto. – cdmckay

risposta

1

Da Ruby 1.9 c'è un modo molto più semplice per risolvere questo problema:

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

Enumerable.chunk gruppo volontà di tutti gli elementi consecutivi di un array insieme dall'uscita di un blocco:

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

Questo è quasi esattamente ciò che richiede OP, tranne che i gruppi risultanti devono essere contati per ottenere l'array di strisce finali.

+0

Grazie! Questo è quello che cercavo in origine. –

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 qui è l'operatore ^ XOR.

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

Quindi, se l'ultimo numero nella matrice è sullo stesso lato della zero come numero in lavorazione, quindi aggiungerlo alla striscia, altrimenti aggiungerlo alla matrice striature ad iniziare una nuova striscia. Si noti che il seno true^true restituisce false che dobbiamo annullare l'intera espressione.

+0

Ma ... stai andando in loop! –

+0

Questo non mi sembra corretto. Stai contando o sommando? – jcrossley3

+0

Oops, ho un po 'di confusione. Questo somma i valori, nella striscia non li conteggia. Hai ragione. Lo aggiusterò. –

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

Ta da! Nessun loop attraverso l'array originale. Sebbene all'interno del duplo sia un MEMPACK, che suppongo possa essere considerato un loop a livello di assemblatore?

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

EDIT:;)

+0

+1 per la commedia, altri hanno ottenuto molti punti in peggio ... –

11

Bene, ecco una versione di una riga, se che ti piace di più:

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

E se anche iniettare è troppo loopy per voi , ecco un modo davvero stupido:

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

Ma penso che sia abbastanza chiaro che si sta meglio con qualcosa di molto più semplice:

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

Oltre ad essere molto più facile da capire e da gestire, la versione "loop" viene eseguito in circa i due terzi del tempo della versione iniettata e circa un sesto del tempo di eval/regexp.

PS: Ecco un'altra versione potenzialmente interessanti:

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} 

Questo utilizza due passaggi, prima costruendo un array di array striscia, quindi convertendo l'array di array a una matrice di dimensioni firmati. In Ruby 1.8.5, questo è in realtà leggermente più veloce della versione inject sopra (sebbene in Ruby 1.9 sia un po 'più lento), ma il ciclo noioso è ancora il più veloce.

+0

Potrebbe usare un commento o due. Ho capito che l'hai spiegato, ma inserire dei commenti nel codice è qualcosa che viene spesso trascurato. –

1

più abuso di stringa, alla Glenn McDonald, solo diversa:

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