3

我正在尝试实现一个 method_missing 来将 $ 转换为其他货币,例如 5.dollars 产生 5,5.yen 产生 0.065 5.euro 6.56 等等。这是我现在可以做到的。现在我需要实现它,但例如做 5.dollars.in(:yen) 。

这就是我现在所拥有的:

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

谁能解释我该怎么做?

PS:我宁愿你不要给我代码,而是一个解释,所以我可以自己确定它是如何完成的。

4

9 回答 9

10

添加货币“美元”和方法

class Numeric
  @@currencies = {'dollar' => 1, '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

  def in(currency)
    singular_currency = currency.to_s.gsub(/s$/, '')
    self / @@currencies[singular_currency]
  end
end
于 2012-03-13T17:28:04.927 回答
5

也许这会更有帮助。这是一个工作示例(注意,我希望您拥有 ActiveSupport [Rails 的一部分] 和 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
于 2012-03-11T03:45:46.043 回答
3

这更像是一个数学问题,而不是计算问题。

每个@@currencies哈希值都被标准化为“美元”:它们的单位是日元/美元欧元/美元卢比/美元。对于5.euro.in(:yen),您只需将欧元/美元除以日元/美元即可将答案表示为日元中的欧元。

要使用 Ruby 进行计算,请method_missing保持方法不变并将类常量更新为 include 'dollar' => 1。添加一个Numeric#in单行计算的方法来解决这个问题。该计算需要以正确的顺序将除法应用于浮点数。

例如5.euro.in(:yen),请记住首先计算5.euro但单位为euro/dollar。接下来的in(:yen)方法必须应用于这个数字的倒数。这将给出一个以日元/欧元为单位的数字,即您想要的结果的倒数。

于 2012-03-20T00:40:09.413 回答
2

您不会只定义一个in将符号参数发送回的方法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

所以,不完全是,因为你不知道当前的金额代表什么——你method_missing假设一切都是美元,即使不是。

这就是为什么有金钱宝石:)

于 2012-03-11T01:22:33.840 回答
1

而不是method_missing在这里使用,它会更容易迭代每种货币并为它们定义单数和复数方法,以委托给您的转换方法。

为了方便起见,我假设您在这里有 ActiveSupport。你可以在没有这些的情况下做任何事情,但是像constantize和关注这样的事情让它变得更容易。

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
于 2012-03-11T01:29:29.313 回答
1

我的方法是基于接受所提出问题的限制(在 Numeric 上扩展 method_missing 实现,尽管@coreyward 表明这对于任何家庭作业问题来说确实是错误的方法)如下:

理解5.euros.in(:yen)可以翻译为:

eur = 5.send(:euros)
eur.send( :in, yen )

本质上发生的是,我们将欧元消息发送到 Numeric 5,然后将in方法发送到 5.euros 的 Numeric 结果,参数为 :yen。

在 method_missing 中,您应该响应euros调用并返回欧元到美元转换的结果,然后(也在 method_missing 中)响应in调用,并将美元(来自上一次调用)转换为作为传递的符号的结果调用的参数in。这将返回正确的值。

当然,只要您的转换因子正确,您就可以转换为/从您想要的任何货币 - 考虑到这个特定问题,转换为美元/从美元转换似乎是最明智的。

于 2012-03-15T02:43:31.093 回答
1

我也在做这门课程,我看到了一些如何完成任务的例子。在某些时候提到了 self.send ,我相信其他人也已经实现了这一点,但我发现这个解决方案对我有用:

https://gist.github.com/2065412

于 2012-03-18T13:15:28.540 回答
0

这是我所做的...

http://pastebin.com/DpE8VAH4

    数字类
      @@currencies = {'yen' => 0.013, '欧元' => 1.292, '卢比' => 0.019, '美元' => 1}
      def method_missing(方法,*arg)
        奇异货币 = method.to_s.gsub(/s$/,'')
        如果@@currencies.has_key?(singular_currency)
          自我 * @@currencies[singular_currency]
        别的
          极好的
        结尾
      结尾
      定义在(arg)
        奇异货币 = arg.to_s.gsub(/s$/,'')
        如果@@currencies.has_key?(singular_currency)
          自我 * @@currencies[singular_currency]
        结尾
      结尾
    结尾

    放 "5.euro = "+5.euro.to_s
    放 "5.euros = "+5.euros.to_s
    把 "5.dollars.in(:euros) = "+5.dollars.in(:euros).to_s
    把 "10.euros.in(:rupees) = "+10.euros.in(:rupees).to_s
  • 将“'dollar' => 1”添加到货币中
  • 在 method_missing 方法中添加一个新的参数 ", *args"
  • 在 Numeric 类中添加一个新方法“in(arg)”
  • 此方法将 self 乘以参数“arg”指定的货币
于 2012-05-30T05:09:08.467 回答
0

首先,安装我的单位库:gem install sy. 然后,定义:

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 >

现在您可以计算:

10 * 10.usd => #<Magnitude: 100 >
100.yen.in :usd #=> #<Magnitude: 1.3 >
1.eur + 1.usd #=> #<Magnitude: 2.29 >

你也可以定义

CENT = SY::Unit.of Money, amount: 0.01.usd
EUROCENT = SY::Unit.of Money, amount: 0.01.eur

进而

12.usd + 90.cent #=> #<Magnitude: 12.9 >
于 2013-06-13T03:20:32.127 回答