用于启用类型调度的习惯用法

问题描述 投票:2回答:1

这里有几个问题,如果其中任何一个得到足够好的回答,我会感到满意。

Background - what is the end goal?

我有兴趣在R中表示日期范围。最小要求是我们代表一个开始和结束日期,可以使用长度为二的日期向量轻松完成。另外,将这个对象扩展到另一个类中会很好

  • 为每个范围提供名称(即字符串)
  • 能够(轻松)使用dplyr::between算子

Shortcomings of my previous approach

我之前将每个范围表示为长度为两个的日期向量。这里的好处是我不依赖于任何外部依赖项,我的数据结构非常轻巧,所以编程并不麻烦。缺点是我厌倦了分别通过beg运算符和参数end[访问日期范围的12(可以说比我们有类实现时更难解释)。

此外,我们最终处理一系列日期范围(即向量),因此在开始嵌套数据结构之前抽象出DateRange是有帮助的。我不想使用长度为两个日期向量的列表,也不希望使用带有两行的data.frame,每列被解释为日期范围。

Where have I looked?

我看过lubridate包,并考虑继承Interval类。从这个继承开始的缺点是我不认为S4对我的用例是必要的。我只需要一些简单的数据属性和一个很好的API来调用dplyr::between

一个理想的解决方案可能只是扩展lubridate::Interval类来保存一个名称,一个结束日期(可能是一个方法,因为这个信息已经通过@start + @.Data存储在Interval中),并扩展dplyr::between以便与所述类很好地配合。

What have I tried?

这是我正在寻找的粗略实现:

# 3 key attributes: beg, end, and name.
MyInterval <- function(beg, end, name = NULL) {
    if (class(beg) == "character") beg <- as.Date(beg)
    if (class(end) == "character") end <- as.Date(end)
    if (is.null(name)) name <- as.character(beg)
    structure(.Data = list('beg' = beg, 'end' = end, 'name' = name), class = "MyInterval")
}

现在,我希望能够重载between运算符,以便我可以如下调用:between(x, MyInterval),我们注意到dplyr::between(x, lo, hi)需要三个参数。为了尝试实现这一点,我尝试设置类型调度,如下所示:

between <- function(...) UseMethod('between')
between.MyInterval <- function(interval, x) {
    if (class(x) == "character") x <- as.Date(x)
    dplyr::between(x, interval$beg, interval$end)
}
between.default <- function(x, lo, hi) dplyr::between(x, lo, hi)

我选择在...原型中使用between的原因是between.MyIntervalbetween.default目前的参数顺序不同。有没有更好的方法来编写这个?我相信行为符合要求(乍一看)

i <- MyInterval("2012-01-01", "2012-12-31")
between(i, "2012-02-01") # Dispatches to between.MyInterval. Returns True as expected.
between(150, 100, 200)   # Dispatches to dplyr::between. Good, we didn't break anything?

Thank you

任何批评都受到欢迎。我知道between是一个不开箱即用的类型调度的函数,所以自己实现它会引发代码气味。

r class date date-range dispatch
1个回答
2
投票

一种可能性是使用data.tableinrange函数。

首先,让我们做一个间隔:

my.interval <- function(beg, end) data.table(beg = as.Date(beg), end = as.Date(end))
mi <- my.interval("2012-01-01", "2012-12-31")

现在你可以这样做:

> as.Date("2012-02-01") %inrange% mi
[1] TRUE

或者定义你自己的inrange函数:

my.inrange <- function(x, intv) data.table::inrange(as.Date(x), intv$beg, intv$end)

有了这个你可以做:

> my.inrange("2012-02-01", mi)
[1] TRUE

正如@Frank评论的那样,您也可以制作my.inrange的中缀变体:

`%my.inrange%` <- my.inrange

现在您也可以使用以下表示法:

"2012-02-01" %my.inrange% mi

这类似于data.tablebetweeninrange函数的中缀符号。

© www.soinside.com 2019 - 2024. All rights reserved.