3

我试图了解如何使用向量构建对象。我认为这很简单,但是当我在对象上使用 c() 时遇到了麻烦。

我们的对象有两个属性,x 和描述符,在这种情况下都是字符串(我的对象将具有不同类型的属性)。我们已经构建了一个构造函数 new_toy_vector。我还没有在这个例子中建立一个便利函数。

new_toy_vector <- function(
  x = character(),
  descriptor = character()) {

  vctrs::vec_assert(x,character())
  vctrs::vec_assert(descriptor, character())

  vctrs::new_vctr(x,
                  descriptor = descriptor,
                  class = "toy_vector")
}



format.toy_vector <- function(x, ...) {
  paste0(vctrs::vec_data(x)," is ", attr(x, "descriptor"))
}

obj_print_data.toy_vector <- function(x) {
  cat(format(x), sep = "\n")
}

c(new_toy_vector("Hello", "Foo"), new_toy_vector("World", "Bar"))
#> Error: No common type for `..1` <toy_vector> and `..2` <toy_vector>.

reprex 包(v0.3.0)于 2020-04-26 创建

然后我尝试用自己创建一个强制,除非由于某种原因没有定义默认方法:

> vec_ptype2.toy_vector.toy_vector <- function(x, y, ...) new_toy_vector()
> c(new_toy_vector("Hello", "Foo"), new_toy_vector("World", "Bar"))
Error: Can't convert <toy_vector> to <toy_vector>.

有什么我想念或误解的想法吗?为什么我不能合并示例中的两个对象?

4

4 回答 4

2

通常,当对象被子集时,属性不会被子集,这不是规则,“名称”属性是一个突出的例子,它不遵循这种做法。要创建行为类似于“名称”的属性,您必须跳过障碍,而{vctrs}旨在为您简化此类任务。

我们使用{vctrs}的方式是使用记录,我们不需要属性:

记录样式对象使用等长向量列表来表示对象的各个组件。最好的例子是 POSIXlt,它的底层是一个包含 11 个字段的列表,例如年、月和日。记录风格的类覆盖 length() 和子集方法来隐藏这个实现细节。

使用上面链接中的示例作为模板,我们可以实现您的案例:

new_toy_vector <- function(
  value = character(),
  descriptor = character()) {
  vctrs::vec_assert(value,character())
  vctrs::vec_assert(descriptor, character())
  vctrs::new_rcrd(list(value = value, descriptor = descriptor), class = "toy_vector")
}


format.toy_vector <- function(x, ...) {
  value <- vctrs::field(x, "value")
  descriptor <- vctrs::field(x, "descriptor")
  paste0('"', value," is ", descriptor, '"')
}

v1 <- new_toy_vector(
  c("Hello", "World"), 
  c("Foo", "Bar"))

v2 <- c(
  new_toy_vector("Hello", "Foo"), 
  new_toy_vector("World", "Bar"))

v1
#> <toy_vector[2]>
#> [1] "Hello is Foo" "World is Bar"

identical(v1, v2)
#> [1] TRUE

v2[2]
#> <toy_vector[1]>
#> [1] "World is Bar"

reprex 包(v0.3.0)于 2021-01-23 创建

请注意,我们不需要创建强制方法,在这种情况下,记录的默认强制方法就足够了。

于 2021-01-23T02:40:53.330 回答
1

添加一个明确 `[.toy_vector`descriptor属性子集。

像这样:

`[.toy_vector` <- function(x,i){
      new_toy_vector(vec_data(NextMethod()),
                     descriptor = attr(NextMethod(), "descriptor")[i])
    }

我不确定如何使用 以这种方式获取“子集”的属性vctrs,或者即使有可能。但是使用这种方法我们基本上可以做一些事情vctrs,然后一些。

请记住,子集 generic 将不再调用该`[.vctrs_vctr`方法,因此您将失去其他vctrs功能(例如使用 子集子类vec_restore())并且可能需要在该`[.toy_vector`方法中实现进一步的修复。

library(vctrs)

new_toy_vector <- function(
  x = character(),
  descriptor = character()) {
  
  vec_assert(x,character())
  vec_assert(descriptor, character())
  
  new_vctr(x,
           descriptor = descriptor,
           class = "toy_vector")
}



format.toy_vector <- function(x, ...) {
  paste0(vec_data(x)," is ", attr(x, "descriptor"))
}

obj_print_data.toy_vector <- function(x) {
  cat(format(x), sep = "\n")
}


vec_ptype2.toy_vector.toy_vector <- function(x, y, ...) {
  new <- c(attr(x, "descriptor"), attr(y, "descriptor"))
  new_toy_vector(descriptor = new)
}

vec_cast.toy_vector.toy_vector <- function(x, to, ...) {
  new_toy_vector(vec_data(x),
                 attr(to, "descriptor"))
}

`[.toy_vector` <- function(x,i){
  new_toy_vector(vec_data(NextMethod()),
                 descriptor = attr(NextMethod(), "descriptor")[i])
}

c(new_toy_vector("Hello", "Foo"), new_toy_vector("World", "Bar")) -> tmp

tmp
#> <toy_vector[2]>
#> Hello is Foo
#> World is Bar

tmp[1]
#> <toy_vector[1]>
#> Hello is Foo

tmp[2]
#> <toy_vector[1]>
#> World is Bar

reprex 包(v0.3.0)于 2021-01-19 创建

于 2021-01-19T12:59:03.197 回答
0

提供一些背景信息,为什么我在这个问题上悬赏(并对这个问题给出一个错误的答案);我可以让串联工作,但这会在其他领域造成麻烦。很明显有些事情是不对的,但是什么?

library(vctrs)

new_toy_vector <- function(
  x = character(),
  descriptor = character()) {
  
  vec_assert(x,character())
  vec_assert(descriptor, character())
  
  new_vctr(x,
           descriptor = descriptor,
           class = "toy_vector")
}



format.toy_vector <- function(x, ...) {
  paste0(vec_data(x)," is ", attr(x, "descriptor"))
}

obj_print_data.toy_vector <- function(x) {
  cat(format(x), sep = "\n")
}


vec_ptype2.toy_vector.toy_vector <- function(x, y, ...) {
  new <- c(attr(x, "descriptor"), attr(y, "descriptor"))
  new_toy_vector(descriptor = new)
}

vec_cast.toy_vector.toy_vector <- function(x, to, ...) {
  new_toy_vector(vec_data(x),
                 attr(to, "descriptor"))
}

z <- c(new_toy_vector("Hello", "Foo"), new_toy_vector("World", "Bar"))
print(z)
#> <toy_vector[2]>
#> Hello is Foo
#> World is Bar

# Subsetting doesn't work properly
z[2]
#> <toy_vector[1]>
#> World is Foo
#> World is Bar

reprex 包(v0.3.0)于 2021-01-18 创建

于 2021-01-18T18:05:23.323 回答
0

我尝试了您的代码,并收到了一条信息更丰富的错误消息:

Error: Can't combine `..1` <toy_vector> and `..2` <toy_vector>.
x Some attributes are incompatible.
ℹ The author of the class should implement vctrs methods.
ℹ See <https://vctrs.r-lib.org/reference/faq-error-incompatible-attributes.html>.
Run `rlang::last_error()` to see where the error occurred.

https://vctrs.r-lib.org/reference/faq-error-incompatible-attributes.html

如果你去关于错误的页面,答案就在那里:vctrs默认情况下不知道如何组合自定义属性。您的向量具有不同的属性:FooBar

如果你试试

a <- new_toy_vector("Hello", "Foo")
b <- new_toy_vector("World", "Foo")
c(a, b)

这会奏效。

于 2021-01-18T17:54:50.207 回答