2009-10-17 3 views
6

Was ist der glatteste, Ruby-ähnliche Weg, dies zu tun?Array-Unterschied erster Ordnung in Ruby

[1, 3, 10, 5].diff 

sollte

[2, 7, -5] 

erzeugen, das heißt, eine Anordnung von Differenzen erster Ordnung. Ich habe eine Lösung gefunden, die ich unten hinzufügen werde, aber es erfordert Rubin 1.9 und ist nicht ganz so glatt. Was ist noch möglich?

Antwort

2

Noch ein anderes way..Seems die kürzeste bisher :)

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

Das ist süß, aber ziemlich ineffizient, wenn die Arrays groß werden ... –

+0

eine gute 1.8 kompatible Lösung. Vielen Dank. – Peter

+1

Sie könnten die Destrukturierungsbindung im Block anstelle der Indizierung verwenden: '{| first, second | erste - zweite} '. Außerdem ist in 1.8.7+ 'self [1 ..- 1]' gleichbedeutend mit 'drop (1)', was das ganze Ding so lesen lässt: 'drop (1) .zip (self) .collect {| first , Sekunde | erste - zweite} '. Auch wenn du Ruby nicht kennst und es nur laut wie Englisch liest, klingt es fast nachvollziehbar, wie eine mathematische Definition des Unterschieds erster Ordnung: "Kombiniere das Array mit sich selbst, indem du den Unterschied errechnest und die Ergebnisse sammelst . " –

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

Beispiel:

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

Nizza Addon. each_cons. – Gishu

2

Das Konzept stammt aus der funktionalen Programmierung, natürlich:

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

[1,3,10,5].diff 

Beachten Sie, dass keine separaten Zwischenvariablen hier brauchen

0

Eine andere Möglichkeit, es zu tun.

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

Ich mag diese funktionalen Stil:

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

EDIT: Ich habe erkannt, dass die reverse völlig unnötig ist. Wenn dies eine funktionale Sprache wäre, hätte ich die Mustererkennung verwendet, aber Ruby unterstützt keine Mustererkennung. Es tut, unterstützt jedoch Destrukturierung binden, die eine gute Näherung für die Mustererkennung in diesem Fall ist.

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

Kein smiley, obwohl.

Ich mag, wie das klingt, wenn Sie es gerade von links nach rechts vorlesen: "Wenden Sie für jedes Paar die Differenz zwischen dem ersten und zweiten Element des Paares an." In der Tat, ich mag normalerweise nicht den Namen collect und bevorzugen map statt, aber in diesem Fall, der liest noch besser: „Für jedes Paar, den Unterschied zwischen seinen Elementen sammeln“

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

Klingt fast wie eine Definition von erster Ordnung Unterschied.

+0

Sieht hübsch aus, aber es ist 4x langsamer als meine ... –

+0

Eigentlich nur 2,6x langsamer, aber immer noch. –

+0

Oh, und auf langen Arrays ist der Unterschied größer. Bei Arrays mit 1000 Nummern ist das ungefähr 10x langsamer als bei meiner schnellsten Version! –

0

Mein schwacher Versuch ...

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

Wie wäre es mit 'inject' anstatt die Liste zu erstellen? Verkürzt die Methode von 3 auf 1 Zeile und ist einfacher zu lesen (imo natürlich, aber es ist zumindest mehr Ruby-ähnlich). – rfunduk

+0

Aber beachten Sie, dass das es auch langsamer machen würde ... –

2

Hier den schnellsten Weg, die ich finden konnte (schneller als alle andere vorgeschlagen hier ab diesem Moment, in beide 1.8 und 1.9):

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

Mit diesem engen Runner-up:

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

Von den anderen hier ist Testrs selbstbeschriebener "schwacher" Versuch der zweitschnellste, aber er ist immer noch langsamer als beide.

Und wenn die Geschwindigkeit keine Rolle spielt, hier ist mein ästhetischer Favorit: hier

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

    def diff 
    dup.diff! 
    end 
end 

Aber das ist (aus Gründen, verstehe ich nicht ganz) eine Größenordnung langsamer als jeder anderer Vorschlag!

+0

Wow, ich hatte keine Ahnung, dass meine Lösung schnell war. Ich habe nur versucht, es mit den wenigen Methoden, die ich kenne, arbeiten zu lassen (tut mir leid, immer noch ein Neuling). genoss diesen Thread, lernte ich über each_cons und injizieren :) – testr

+0

Wenn Ausführung Geschwindigkeit das Hauptanliegen war, würden Sie wahrscheinlich nicht mit Ruby zu beginnen, aber ich denke, es ist immer interessant, die Tests durchzuführen. –

+0

Die meisten vorhandenen Ruby-Implementierungen sind bei Methodenaufrufen ziemlich beschissen. Außerdem sind die weit verbreiteten Implementierungen bei der Objektzuordnung ziemlich beschissen. In Ihrer Lösung werden Sie für jedes Element des Arrays einmal rekursiv, und Sie vergeben zwei neue Arrays pro Schritt, so dass Sie insgesamt eine Call-Stack-Tiefe von n, 6 Methodenaufrufe pro Schritt (insgesamt 6n) und insgesamt 2n Arrays haben zugeordnet. All dies ist ziemlich langsam auf den derzeit weit verbreiteten VMs. Der neue (noch nicht veröffentlichte) JRuby JIT-Compiler hat (unter anderem) die Eliminierung von Array-Zuweisungen und könnte dies möglicherweise dramatisch beschleunigen. –

2

Minor Variante Jörg W Mittag suchen:

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

Schön, das ist leicht am klarsten (wenn 1.9 in Ordnung ist). 1,6x langsamer als meine Version in meinen Tests ... –

+0

Ich denke, es funktioniert in 1.8.7, aber nicht 1.8.6 – jes5199