2013-02-08 17 views
10

Ich bin auf der Suche nach einer Möglichkeit, ein einzelnes Objekt in Ruby zu "mappen".Ruby map() für ein einzelnes Objekt

Ich möchte diese Funktion aufrufen und einen Block übergeben, das Objekt wird dem Block übergeben, und dann wird das Ergebnis des Blocks an den Aufrufer zurückgegeben. Genau was Map tut, aber für ein einzelnes Element.

Die Motivation ist, dass Sie manchmal Objekte generieren, die nur verwendet werden, um etwas anderes zu konstruieren. Das ursprüngliche Objekt wird dann nicht mehr benötigt. Es wäre schön, die Umwandlung einfach in einen Block zu setzen und das Temporäre zu eliminieren.

Als künstliches Beispiel nehmen wir an, dass ich eine ganze Zahl erstellen möchte, die die Kombination von Monat und Jahr darstellt. Für das heutige Datum, würde der Code in etwa so aussehen:

day = Date.today 
month_number = day.year * 100 + day.month 

Ich mag es würde, wenn ich so etwas wie tun könnte:

month_number = Date.today.some_function { |d| d.year * 100 + d.month } 

Aber ich weiß nicht, was ‚some_function‘ ist (oder wenn es überhaupt existiert).

Wenn es etwas mehr Ruby gibt, so etwas zu handhaben, bin ich ganz Ohr. Ich kenne Affenkurs-Klassen, aber ich möchte mit solchen Fällen umgehen, die etwas flüchtiger sind.

+0

[ 'Objekt # tap'] (http://ruby-doc.org/core-1.9.3/Object.html#method-i-tap) ist so nah, aber es doesn‘ t den Wert des Blocks zurückgeben ... – maerics

+1

Etwas wie? m = Lambda {| d | d.year * 100 + d.month} .call (Date.today) – Kaeros

+0

Siehe @jondavidjohns Antwort. Eingebaut und ohne Proc-Bindung. –

Antwort

13

instance_eval ist, was Sie suchen.

month_number = Date.today.instance_eval { |d| d.year * 100 + d.month } 

Die |d| ist ebenfalls optional und self standardmäßig auf den Objektkontext.

Dies kann Ihre Anforderungen in einer kompakteren Weise erfüllen.

month_number = Date.today.instance_eval { year * 100 + month } 
+1

Danke! Genau das habe ich gebraucht. –

+0

Es sieht aus wie die | d | ist auch optional (self wird die Instanz während der Blockausführung sein). Also wird das Folgende auch funktionieren: Date.today.instance_eval {Jahr * 100 + Monat} –

+0

Danke, es hat für mich funktioniert. –

3

Rubys builtin Object#tap der Nähe ist, aber es nicht den Wert des Blocks zurück.

Hier ist eine Idee:

class Object 
    def sap 
    yield self 
    end 
end 

eleven = 10.sap { |x| x + 1 } # => 11 
month_number = Date.today.sap { |d| d.year * 100 + d.month } # => 201202 
+0

Warum würdest du Affen plappern, wenn du nicht halbwegs bist? Siehe meine Antwort unten. – jondavidjohn

+1

+1 für eine nette, kurze Syntax, aber ich denke, @jondavidjohns Verwendung von instance_eval ist korrekter, da er eingebaut ist. –

+2

Während 'instance_eval' vergleichbar funktioniert, werden auch die Membervariablen des Zielobjekts, breaking, angezeigt die Schnittstelle/Implementierungsbarriere. Dies ist nicht so sehr ein Sicherheitsproblem als eine Chance, versehentlich den Fuß wegzublasen. IMHO Monkey Patching ist in diesem Fall gerechtfertigt. – maerics

15

instance_eval wie in jondavidjohn ‚s Antwort zu verwenden ist ein Weg zu gehen, aber es hat Overhead für self Neuzuordnung. Ein solches Feature war einmal proposed in Ruby core, wurde aber abgelehnt und wurde zurückgezogen. Unter Verwendung der Lösung dort präsentiert von einem der Ruby-Entwickler knu (Akinori MUSHA), könnte man wie folgt schreiben:

month_number = Date.today.tap{|d| break d.year * 100 + d.month} 

tap verwenden, das einzige Extra, was Sie tun müssen, um break am Anfang des Blocks gesetzt wird .


require 'benchmark' 

n = 500000 
Benchmark.bm do |x| 
    x.report{n.times{Date.today.instance_eval{year * 100 + month}}} 
    x.report{n.times{Date.today.instance_eval{|d| d.year * 100 + d.month}}} 
    x.report{n.times{Date.today.tap{|d| break d.year * 100 + d.month}}} 
end 

     user  system  total  real 
    2.130000 0.400000 2.530000 ( 2.524296) 
    2.120000 0.400000 2.520000 ( 2.527134) 
    1.410000 0.390000 1.800000 ( 1.799213) 
+1

+1 für die Antwort am wahrscheinlichsten von der Kernmannschaft bevorzugt werden. Ich lerne immer noch einige von Rubys Hinein und Hinein und das Verhalten, aus Blöcken zu brechen, ist neu für mich. In diesem Fall finde ich das Verhalten der Sprache ehrlich widerwärtig. –

+1

+1 für die Benchmark. Vielen Dank! –

Verwandte Themen