Добавление новых столбцов в справочную таблицу data.table внутри функции, которая не всегда работает

При написании пакета, который опирается на data.table, я обнаружил некоторое нечетное поведение. У меня есть функция, которая удаляет и переупорядочивает некоторую колонку по ссылке, и она работает просто отлично, то есть переданный мной data.table был изменен без назначения вывода функции. У меня есть еще одна функция, которая добавляет новые столбцы, но эти изменения не всегда сохраняются в data.table, который был передан.

Вот небольшой пример:

library(data.table)  # I'm using 1.9.4
test <- data.table(id = letters[1:2], val=1:2)
foobar <- function(dt, col) {
    dt[, (col) := 1]
    invisible(dt)
}

test
#  id val
#1: a   1
#2: b   2
saveRDS(test, "test.rds")
test2 <- readRDS("test.rds")
all.equal(test, test2)
#[1] TRUE
foobar(test, "new")
test
#  id val new
#1: a   1   1
#2: b   2   1
foobar(test2, "new")
test2
#  id val
#1: a   1
#2: b   2

Что случилось? Чем отличается test2? Я могу изменить существующие столбцы на месте:

foobar(test, "val")
test
#  id val new
#1: a   1   1
#2: b   1   1
foobar(test2, "val")
test2
#  id val
#1: a   1
#2: b   1

Но добавление в test2 все еще не работает:

foobar(test2, "someothercol")
.Last.value
#  id val someothercol
#1: a   1            1
#2: b   1            1
test2
#  id val
#1: a   1
#2: b   1

Я не могу зафиксировать все случаи, когда я вижу это поведение, но сохранение и чтение из RDS - это первый случай, который я могу достоверно реплицировать. Запись и чтение из CSV не похоже на ту же проблему.

Является ли это проблемой указателя ala этой проблемы, например, сериализация data.table уничтожает перенапряженные указатели? Есть ли простой способ восстановить их? Как я могу проверить их внутри своей функции, чтобы я мог восстановить указатели или ошибку, если операция не будет работать?

Я знаю, что я могу назначить вывод функции в качестве обходного пути, но это не очень data.table -y. Разве это не создало бы временную копию в памяти?

Ответ на решение Arun

Арун дал указание, что это действительно проблема с указателем, которая может быть диагностирована с помощью truelength и исправлена ​​с помощью setDT или alloc.col. Я столкнулся с проблемой, инкапсулирующей его решение в функцию (продолжая с кода выше):

func <- function(dt) {if (!truelength(dt)) setDT(dt)}
func2 <- function(dt) {if (!truelength(dt)) alloc.col(dt)}
test2 <- readRDS("test.rds")
truelength(test2)
#[1] 0
truelength(func(test2))
#[1] 100
truelength(test2)
#[1] 0
truelength(func2(test2))
#[1] 100
truelength(test2)
#[1] 0

Таким образом, похоже, что локальная копия внутри функции правильно изменена, но эталонная версия - нет. Почему бы и нет?

Ответ 1

Является ли это проблемой указателя ala этой проблемы, например, сериализация data.table уничтожает перенапряженные указатели?

Да, загрузка с диска устанавливает внешний указатель на NULL. Нам снова придется переназначать.

Есть ли простой способ их восстановить?

Да. Вы можете проверить для truelength() таблицы data.table, и если он 0, используйте setDT() или alloc.col() на нем.

truelength(test2) # [1] 0
if (!truelength(test2))
    setDT(test2)
truelength(test2) # [1] 100

foobar(test2, "new")
test2[]
#    id val new
# 1:  a   1   1
# 2:  b   2   1

Это, вероятно, следует рассматривать как часто задаваемый вопрос (не помню, чтобы видеть его там).
Уже в FAQ в разделе предупреждений.