Ruby – method_missing
-
11-12-2019 - |
Frage
Ich versuche, eine method_missing für die Umrechnung von $ in andere Währungen zu implementieren, da 5 Dollar 5 ergibt, 5 Yen 0,065, 5 Euro 6,56 und so weiter.Das kann ich jetzt tun.Jetzt muss ich es implementieren, aber zum Beispiel 5.dollars.in(:yen) machen.
Das ist es, was ich gerade habe:
class Numeric
@@currencies = {'yen' => 0.013, 'euro' => 1.292, 'rupee' => 0.019}
def method_missing(method_id)
singular_currency = method_id.to_s.gsub( /s$/, '')
if @@currencies.has_key?(singular_currency)
self * @@currencies[singular_currency]
else
super
end
end
end
Kann mir jemand erklären, wie ich das machen kann?
PS:Ich möchte lieber, dass Sie mir nicht den Code geben, sondern eine Erklärung, damit ich selbst bestimmen kann, wie es gemacht wird.
Lösung
Perhaps this will be of more help. It's a working example (note, I'm expecting you to have ActiveSupport [part of Rails] and Ruby 1.9.2+):
require 'rubygems'
# This is allowing us to do the `pluralize` calls below
require 'active_support/inflector'
module Currency
CONVERSION_TABLE = { dollars: { dollars: 1, euros: 0.75 }, euros: { dollars: 1.3333334, euros: 1 } }.freeze
attr_accessor :currency
def method_missing(method_name, *args, &block)
# standardize on pluralized currency names internally so both singular
# and plural methods are handled
method_name = method_name.to_s.pluralize.to_sym
# Use the "from" keys in the conversion table to verify this is a valid
# source currency
if CONVERSION_TABLE.key?(method_name)
@currency = method_name
self # return self so a call to `1.dollar` returns `1` and not `:dollars`
else
super
end
end
# Convert `self` from type of `@currency` to type of `destination_currency`, mark the result with
# the appropriate currency type, and return. Example:
def to(destination_currency)
# Again, standardize on plural currency names internally
destination_currency = destination_currency.to_s.pluralize.to_sym
# Do some sanity checking
raise UnspecifiedSourceCurrency unless defined?(@currency)
raise UnsupportedDestinationCurrency unless CONVERSION_TABLE.key?(destination_currency)
# Do the actual conversion, and round for sanity, though a better
# option would be to use BigDecimal which is more suited to handling money
result = (self * CONVERSION_TABLE[@currency][destination_currency]).round(2)
# note that this is setting @currency through the accessor that
# was created by calling `attr_accessor :currency` above
result.currency = destination_currency
result
end
end
class Numeric
# Take all the functionality from Currency and mix it into Numeric
#
# Normally this would help us encapsulate, but right now it's just making
# for cleaner reading. My original example contained more encapsulation
# that avoided littering the Numeric clas, but it's harder for a beginner
# to understand. For now, just start here and you will learn more later.
include Currency
end
p 5.euros.to(:dollars) #=> 6.67
p 0.25.dollars.to(:euro) #=> 0.19
p 1.dollar.to(:euros).to(:dollar) #=> 1.0
Andere Tipps
Währung "Dollar" und
Dies ist eher ein mathematisches als ein rechnerisches Problem.
Jedes von den @@currencies
Hash-Werte werden auf „Dollar“ normalisiert:ihre Einheiten sind Yen/Dollar, Euro Dollar, Rupie/Dollar.Für 5.euro.in(:yen)
, Sie müssen nur dividieren Euro Dollar von Yen/Dollar um die Antwort als Euro in Yen auszudrücken.
Um dies mit Ruby zu berechnen, verlassen Sie die method_missing
Methode unverändert und aktualisieren Sie die Klassenkonstante, um sie einzuschließen 'dollar' => 1
.Füge hinzu ein Numeric#in
Methode mit einer einzeiligen Berechnung zur Lösung dieses Problems.Bei dieser Berechnung muss eine Division in der richtigen Reihenfolge auf eine Gleitkommazahl angewendet werden.
Für 5.euro.in(:yen)
Denken Sie zum Beispiel daran 5,-Euro wird zuerst berechnet, hat aber Einheiten von Euro Dollar.Der in(:Yen) Die nächste Methode muss auf den Kehrwert dieser Zahl angewendet werden.Dies ergibt eine Zahl mit Einheiten in Yen/Euro, der Kehrwert Ihres gewünschten Ergebnisses.
Würden Sie nicht einfach eine Methode namens definieren in
an den der Symbolparameter zurückgesendet wurde self
?
irb(main):057:0> 5.dollar.in(:euro)
=> 6.46
irb(main):065:0> 5.euro.in(:dollar)
=> 6.46 # Which is wrong, by the way
Also nicht ganz, denn Sie wissen nicht, wie hoch der aktuelle Betrag ist – Ihr method_missing
geht davon aus, dass alles in Dollar ist, auch wenn das nicht der Fall ist.
Deshalb gibt es das Geldjuwel :)
Anstatt zu benutzen method_missing
Hier wäre es einfacher, alle Währungen zu durchlaufen und Singular- und Pluralmethoden zu definieren, um sie an Ihre Umrechnungsmethode zu delegieren.
Der Einfachheit halber gehe ich davon aus, dass Sie hier ActiveSupport haben.Sie könnten auf alles verzichten, aber Dinge wie constantize
und Bedenken machen es einfacher.
module DavesMoney
class BaseMoney
# your implementation
end
class DollarConverter < BaseMoney
def initialize(value)
@value = value
end
def to(:currency)
# implemented in `BaseMoney` that gets extended (or included)
end
end
end
module CurrencyExtension
extend ActiveSupport::Concern
SUPPORTED_CURRENCIES = %w{ dollar yen euro rupee }
included do
SUPPORTED_CURRENCIES.each do |currency|
define_method :"#{currency}" do
return "#{currency}_converter".constantize.new(self)
end
alias :"#{currency.pluralize}" :"#{currency}"
end
end
end
# extension
class Numeric
include CurrencyExtension
end
Mein Ansatz hierzu basiert auf der Akzeptanz der Grenzen des gestellten Problems (Erweitern einer method_missing-Implementierung auf Numeric, auch wenn dies, wie @coreyward angibt, für alles wirklich der falsche Ansatz ist nicht eine Hausaufgabenaufgabe) war wie folgt:
Das verstehen 5.euros.in(:yen)
kann übersetzt werden als:
eur = 5.send(:euros)
eur.send( :in, yen )
Was im Wesentlichen passiert, ist, dass wir die Euro-Nachricht an Numeric 5 senden und dann die in
Methode zum numerischen Ergebnis von 5, Euro mit einem Parameter von: Yen.
In method_missing sollten Sie auf Folgendes antworten euros
Rufen Sie auf und geben Sie das Ergebnis einer Umrechnung von Euro in Dollar zurück, und antworten Sie dann (auch in method_missing) auf die in
Aufruf mit den Ergebnissen der Konvertierung der Dollars (aus dem vorherigen Aufruf) in das Symbol, das als Parameter an übergeben wurde in
Anruf.Dadurch wird der richtige Wert zurückgegeben.
Natürlich können Sie in jede beliebige Währung umrechnen, solange Ihre Umrechnungsfaktoren korrekt sind. Angesichts der gegebenen Umstände für dieses spezielle Problem schien die Umrechnung in/von Dollar am sinnvollsten zu sein.
Ich mache diesen Kurs auch und ich sah ein paar Beispiele, wie man die Aufgabe erfüllt.In irgendeinem Punkt wurde sich selbst erwähnt und ich glaube, dass jemand anderes dies auch implementiert hat, aber ich fand diese Lösung für mich:
hier ist das, was ich getan habe ...
http://pastein.com/dpe8vah4 generasacodicetagpre.
- .
- add "'' dollar '=> 1" in Währungen
- Fügen Sie ein neues Argument in der Methode_missing-Methode ", * Args" hinzu
- Fügen Sie eine neue Methode "in (arg)" in die numerische Klasse hinzu
- Diese Methode multiplizieren sich selbst von der von dem Argument "arg" angegebenen Währung "arg"
Installieren Sie zunächst meine Einheitenbibliothek: gem install sy
.Dann definieren Sie:
require 'sy'
Money = SY::Quantity.dimensionless #=> #<Quantity:Money>
USD = SY::Unit.standard of: Money #=> #<Unit:USD of Money >
YEN = SY::Unit.of Money, amount: 0.013 #=> #<Unit:YEN of Money >
EUR = SY::Unit.of Money, amount: 1.292 #=> #<Unit:EUR of Money >
INR = SY::Unit.of Money, amount: 0.019 #=> #<Unit:INR of Money >
Und jetzt können Sie berechnen:
10 * 10.usd => #<Magnitude: 100 >
100.yen.in :usd #=> #<Magnitude: 1.3 >
1.eur + 1.usd #=> #<Magnitude: 2.29 >
Sie können auch definieren
CENT = SY::Unit.of Money, amount: 0.01.usd
EUROCENT = SY::Unit.of Money, amount: 0.01.eur
Und dann
12.usd + 90.cent #=> #<Magnitude: 12.9 >