在 S4 class 对象中实现基本算术

Implementing basic arithmetic in S4 class object

我正在按以下方式创建 money class 的 S4 对象:

# Create class ------------------------------------------------------------

# Create S4 class object holding money and export to generator function
setClass(Class = "money",
         slots = list(currency = "character",
                      value = "numeric")) -> money

我稍后将为此定义 show 方法:

# Methods -----------------------------------------------------------------

# Create show method
setMethod("show",
          "money",
          function(object) {
              cat(switch(object@currency,
                         GBP = intToUtf8(163)),
                  format(
                      x = round(object@value, 2),
                      trim = TRUE,
                      big.mark = ",",
                      big.interval = 3
                  ),
                  sep = "")
          })

预览

到目前为止它按承诺工作:

# Create test object
tst_fig <- new(Class = "money",
               value = 1e6,
               currency = "GBP")

# Show the object
tst_fig
# £1,000,000

问题

我想对该对象启用基本算术:

>> tst_fig + 1e6
Error in tst_fig + 1000000 : non-numeric argument to binary operator

想要的结果

> tst_fig + 1e6
# £2,000,000

尝试次数

这自然不行:

>> setMethod("+",
...           "money",
...           function(object, x) {
...               object@value + x
...           })
Error in conformMethod(signature, mnames, fnames, f, fdef, definition) : 
  in method for ‘+’ with signature ‘e1="money"’: formal arguments (e1 = "money", e2 = "money") omitted in the method definition cannot be in the signature

旁注

在 S3 中有类似的 excellent answer provided by @Roland 实现金钱 class;在这个问题的上下文中,我有兴趣创建 S4 class,它会以类似的方式运行,除了好奇之外没有任何特定原因。关键要求是 isS4() 在那个对象上 returns TRUE.

我的意思类似的方式:

它的打印效果就像格式精美的钱,但允许对普通数字执行的所有操作。

我在自己的问题 here 中遇到了如何做到这一点。我通常使用 setMethod('Arith') 方法,因为当您打算实现多个操作时它更简洁。如果您搜索文档 ?Arith,您会看到它列出了不同的操作以及其他 S4 组泛型。

正如错误提示,您需要为 Arith 方法定义 e1e2。在您的具体情况下,以下工作。

注意 - 要获得所需的输出(即 money class 对象),您需要创建一个新的 money 对象.

setMethod("+", 
    c(e1="money", e2="numeric"), 
    function(e1, e2){
        new(Class = "money", value = e1@value + e2, currency = e1@currency)
    }
)

tst_fig + 1e6
[1] £2e+06

但是,正如我所说,您可能想要更通用、更简洁的版本,它使用 .Generic 来解释您正在使用的 Arith 方法。

setMethod("Arith", 
    c(e1="money", e2="numeric"),
    function(e1, e2)
    {
        op = .Generic[[1]]
        switch(op,
            `+` = return(new(Class = "money", value = e1@value + e2, currency = e1@currency))
        )
    }
)