R:将列表派生的 class 显示为 tibble 中的向量

R: Display list-derived class as vector in tibble

我必须经常使用 1 + 1i + 1j + 1ij 形式的多维复数(举一个二维示例),我正在尝试定义一个简单的 class 以促进常见计算。

在处理多个这样的数字时,我选择使用向量列表表示。但是,我仍然希望这个派生列表 class 看起来和感觉起来像一个向量。

我对简单打印没问题:

ncmplx <- setClass("ncmplx", contains = "list")

format.ncmplx <- function(x, ...) {
  f <- function(y) {
    paste(format(as.numeric(y), ...), c('', 'i', 'j', 'ij'), 
          sep = '', collapse = '+')
  }
  unlist(lapply(x, f))
}

setMethod("show", "ncmplx", function(object) {
    print(format(object))
})

a <- ncmplx(list(1:4, 2:5))

使用 print(a) 显示 [1] "1+2i+3j+4ij" "2+3i+4j+5ij" 这基本上是我想要的。

问题是,如何在 tibble 中获得类似的显示?我遵循了这个指南:https://cran.r-project.org/web/packages/tibble/vignettes/extending.html 但它对所有内容都使用了一个基础向量,而不是一个列表,这似乎使事情变得更容易。这是我尝试过的:

library(tibble)

pillar_shaft.ncmplx <- function(x, ...) {
  out <- format(x)
  out[is.na(x)] <- NA
  pillar::new_pillar_shaft_simple(out, align = "right")
}

a <- ncmplx(list(1:4, 2:5))
b <- tibble(x = 1:2, a = a)

print(b)

但结果仍然显示 <int [4]> 形式的基于列表的摘要,而不是我想要的 1+2i+3j+4ij 格式。

我可以破解 obj_sum() 函数,这样我的列表内容摘要实际上是列表内容的直接表示,但这似乎是迂回的。有没有办法告诉 tibble 使用 format() 函数而不是 obj_sum() 来简单地格式化我的列表?

这最终成为了解 vctrs 包的好机会。简而言之,vctrs 使用 new_rcrd() 函数定义了一个 record-style class。我根据这个小插图中的说明从上面重新创建了我的基本二维复数:https://github.com/r-lib/vctrs/blob/master/vignettes/s3-vector.Rmd

new_cmplx2 <- function(rr = double, ri = double(), ir = double(), ii = double) {
  vec_assert(rr, ptype = double())
  vec_assert(ri, ptype = double())
  vec_assert(ir, ptype = double())
  vec_assert(ii, ptype = double())

  new_rcrd(list(rr = rr, ri = ri, ir = ir, ii = ii), class = "vctrs_cmplx2")
}

cmplx2 <- function(rr = 0, ri = 0, ir = 0, ii = 0) {
  c(rr, ri, ir, ii) %<-% vec_cast_common(rr, ri, ir, ii, .to = double())
  c(rr, ri, ir, ii) %<-% vec_recycle_common(rr, ri, ir, ii)

  new_cmplx2(rr, ri, ir, ii)
}

format.vctrs_cmplx2 <- function(x, ...) {
  rr <- field(x, "rr")
  ri <- field(x, "ri")
  ir <- field(x, "ir")
  ii <- field(x, "ii")

  out <- paste0(rr, "+", ri, "i+", ir, "j+", ii, "ij")
  out[is.na(rr) | is.na(ri) | is.na(ir) | is.na(ii)] <- NA

  out
}

vec_ptype_abbr.vctrs_cmplx2 <- function(x) "cmplx2"
vec_ptype_full.vctrs_cmple2 <- function(x) "complex2d"

由于 vctrs 是一个 tidyverse 项目,因此与 tibble 的集成完全按预期工作也就不足为奇了:

tibble(x = 1:2, a = cmplx2(rr=1:2))

这会导致 a 的预期列条目:1+0i+0j+0ij, 2+0i+0j+0ij