2009-10-17 13 views
6

Qual è il modo più intelligente, più simile a Ruby per farlo?Differenza dell'array del primo ordine in Ruby

[1, 3, 10, 5].diff 

dovrebbe produrre

[2, 7, -5] 

cioè una serie di differenze del primo ordine. Ho trovato una soluzione che aggiungerò qui di seguito, ma richiede Ruby 1.9 e non è poi così liscia. cos'altro è possibile?

risposta

2

Ancora un altro way..Seems più breve finora :)

module Enumerable 
    def diff 
     self[1..-1].zip(self).map {|x| x[0]-x[1]} 
    end  
    end 
+0

Questo è carino, ma piuttosto inefficiente se gli array diventano grandi ... –

+0

una buona soluzione compatibile 1.8. Grazie. – Peter

+1

È possibile utilizzare il binding distruttivo nel blocco anziché l'indicizzazione: '{| first, second | primo secondo} '. Inoltre, in 1.8.7+ 'self [1 ..- 1]' è equivalente a 'drop (1)', rendendo l'intera lettura come segue: 'drop (1) .zip (self) .collect {| first , secondo | primo secondo} '. Di nuovo, anche se non conosci Ruby e lo leggi solo ad alta voce come in inglese, sembra quasi comprensibile, come una definizione matematica della differenza del primo ordine: "Combina l'array con se stesso spostato di uno calcolando la differenza e raccogliendo i risultati ". –

1
# Attempt, requires ruby 1.9. 
module Enumerable 
    def diff 
    each_cons(2).with_object([]){|x,array| array << x[1] - x[0]} 
    end 
end 

Esempio:

[1,3,10,5].diff 
=> [2, 7, -5] 
+0

Nizza addon. each_cons. – Gishu

2

Il concetto viene dalla programmazione funzionale, naturalmente:

module Enumerable 
    def diff 
    self.inject([0]) { |r,x| r[-1] += x; r << -x } [1..-2] 
    end 
end 

[1,3,10,5].diff 

Si noti che non è necessario alcun variabili intermedie separati qui

0

Un altro modo per farlo.

module Enumerable 
    def diff 
    result = [] 
    each_with_index{ |x, i| 
     return result if (i == (self.length-1)) 
     result << self[i+1] - x 
    } 
    end 
end 
5

mi piace questo stile funzionale:

module Enumerable 
    def diff 
    each_cons(2).map {|pair| pair.reverse.reduce :-} 
    end 
end 

EDIT: Ho appena realizzato che il reverse è del tutto inutile. Se si trattasse di un linguaggio funzionale, avrei utilizzato la corrispondenza dei modelli, ma Ruby non supporta la corrispondenza dei modelli. Lo fa, tuttavia, supporta il bind di destrutturazione, che è un'approssimazione abbastanza buona per la corrispondenza del modello in questo caso.

each_cons(2).map {|first, second| second - first} 

Nessun smile, comunque.

Mi piace come suona questa cosa se la leggete ad alta voce da sinistra a destra: "Per ogni coppia, applica la differenza tra il primo e il secondo elemento della coppia." In realtà, io di solito non mi piace il nome collect e preferiscono map invece, ma in questo caso, che recita ancora meglio: "Per ogni coppia, raccogliere la differenza tra i suoi elementi"

each_cons(2).collect {|first, second| second - first} 

Sembra quasi una definizione della differenza del primo ordine.

+0

Sembra carino, ma è 4x più lento del mio ... –

+0

In realtà solo 2.6x più lento, ma comunque. –

+0

Oh, e su array lunghi la differenza è più grande. Con gli array a 1000 numeri questo è circa 10 volte più lento della mia versione più veloce! –

0

mio debole tentativo ...

module Enumerable 
    def diff 
    na = [] 
    self.each_index { |x| r << self[x]-self[x-1] if x > 0 } 
    na 
    end 
end 

p [1,3,10,5].diff #returned [2, 7, -5] 
+0

Che ne dici di usare 'inject' per compilare la lista? Accorcia il metodo da 3 a 1 riga ed è più facile da leggere (imo, ovviamente, ma è almeno più simile a ruby). – rfunduk

+0

Ma nota che ciò renderebbe anche più lento ... –

2

Ecco il modo più veloce che ho trovato (più veloce di tutti gli altri hanno suggerito qui da questo momento, sia 1.8 e 1.9):

module Enumerable 
    def diff 
    last=nil 
    map do |x| 
     r = last ? x - last : nil 
     last = x 
     r 
    end.compact 
    end 
end 

Con questa close runner-up:

012.
module Enumerable 
    def diff 
    r = [] 
    1.upto(size-1) {|i| r << self[i]-self[i-1]} 
    r 
    end 
end 

Degli altri qui, il tentativo "debole" descritto da Testr è il più veloce, ma è ancora più lento di uno di questi.

E se la velocità non è oggetto, ecco il mio preferito estetica:

module Enumerable 
    def diff! 
    [-shift+first] + diff! rescue [] 
    end 

    def diff 
    dup.diff! 
    end 
end 

Ma questo è (per ragioni che non capisco del tutto) un ordine di grandezza più lento rispetto a qualsiasi altro suggerimento qui!

+0

wow, non avevo idea che la mia soluzione fosse veloce. Stavo solo cercando di farlo funzionare con i pochissimi metodi che ho familiarità con (mi dispiace, ancora un principiante). ho apprezzato questo thread, ho imparato a conoscere ogni_cons e iniettato :) – testr

+0

Se la velocità di esecuzione era la preoccupazione principale, probabilmente non useresti Ruby per cominciare, ma penso che sia sempre interessante eseguire i test. –

+0

La maggior parte delle implementazioni di Ruby esistenti sono piuttosto schifose nelle invocazioni dei metodi. Inoltre, le implementazioni ampiamente utilizzate sono piuttosto schifose nell'assegnazione degli oggetti. Nella tua soluzione ricorsi una volta per ogni elemento dell'array, e assegni due nuovi array per ogni passo, quindi in tutto hai una profondità di stack delle chiamate di n, 6 chiamate di metodo per step (6n totali) e un totale di array 2n allocato. Tutto ciò è piuttosto lento sulle attuali macchine virtuali diffuse. Il nuovo compilatore JRuby JIT (non ancora rilasciato) ha l'eliminazione dell'allocazione dell'array (tra le altre cose) e potrebbe potenzialmente velocizzarlo notevolmente. –

2

variazione minore su Jörg W Mittag di:

module Enumerable 
    def diff 
    each_cons(2).map{|a,b| b-a} 
    end 
end 
+0

Bello, questo è facilmente il più chiaro (se 1.9 è OK). 1.6x più lento della mia versione nei miei test ... –

+0

Penso che funzioni in 1.8.7 ma non 1.8.6 – jes5199

Problemi correlati