Зачем кому-то использовать {} для инициализации пустого объекта в R?

Кажется, что некоторые программисты используют:

a = {}
a$foo = 1
a$bar = 2

Какая польза от a = list(foo = 1, bar = 2)?

Зачем использовать {}? Это выражение возвращает NULL, поэтому назначение NULL будет делать то же самое, не так ли?

Ответ 1

Ваш первый запрос

Зачем использовать {}, это выражение возвращает NULL, поэтому назначение NULL будет делать то же самое, не так ли?

Да, a <- NULL дает тот же эффект. Использование {} скорее всего будет личного стиля.


NULL

NULL - это, вероятно, самый универсальный и запутанный объект R. Из определения R-языка NULL:

Он используется всякий раз, когда необходимо указать или указать, что объект отсутствует. Его не следует путать с вектором или списком нулевой длины.

Объект NULL не имеет типа и не имеет модифицируемых свойств. В R есть только один объект NULL, к которому относятся все экземпляры. Для проверки использования NULL is.null. Вы не можете устанавливать атрибуты в NULL.

Строго говоря, NULL - это просто NULL. И это единственное, что is.null возвращает TRUE. Однако, согласно " ?NULL:

Объекты со значением NULL могут быть изменены операторами замещения и будут привязаны к типу правой части.

Таким образом, хотя он не идентичен вектору length-0 с законным режимом (не все режимы в R разрешены в векторе, ?mode чтения для полного списка режимов и ?vector для того, что является законным для вектора) это гибкое принуждение часто приводит к тому, что он ведет себя как вектор длиной-0:

## examples of atomic mode
integer(0)  ## vector(mode = "integer", length = 0)
numeric(0)  ## vector(mode = "numeric", length = 0)
character(0)  ## vector(mode = "character", length = 0)
logical(0)  ## vector(mode = "logical", length = 0)

## "list" mode
list()  ## vector(mode = "list", length = 0)

## "expression" mode
expression()  ## vector(mode = "expression", length = 0)

Вы можете выполнить векторную конкатенацию:

c(NULL, 0L)  ## c(integer(0), 0L)
c(NULL, expression(1+2))  ## c(expression(), expression(1+2))
c(NULL, list(foo = 1))  ## c(list(), list(foo = 1))

Вы можете вырастить вектор (как и в своем вопросе):

a <- NULL; a[1] <- 1; a[2] <- 2
## a <- numeric(0); a[1] <- 1; a[2] <- 2

a <- NULL; a[1] <- TRUE; a[2] <- FALSE
## a <- logical(0); a[1] <- TRUE; a[2] <- FALSE

a <- NULL; a$foo <- 1; a$bar <- 2
## a <- list(); a$foo <- 1; a$bar <- 2

a <- NULL; a[1] <- expression(1+1); a[2] <- expression(2+2)
## a <- expression(); a[1] <- expression(1+1); a[2] <- expression(2+2)

Использование {} для генерации NULL аналогично expression(). Хотя это не идентично, принуждение во время выполнения, когда вы потом что-то с этим делаете, делает их неразличимыми. Например, при составлении списка будет работать любое из следующего:

a <- NULL; a$foo <- 1; a$bar <- 2
a <- numeric(0); a$foo <- 1; a$bar <- 2  ## there is a warning
a <- character(0); a$foo <- 1; a$bar <- 2  ## there is a warning
a <- expression(); a$foo <- 1; a$bar <- 2
a <- list(); a$foo <- 1; a$bar <- 2

Для вектора длины-0 с атомным режимом предупреждение возникает во время принудительного принуждения (потому что изменение от "атомного" до "рекурсивного" слишком значимо):

#Warning message:
#In a$foo <- 1 : Coercing LHS to a list

Мы не получаем предупреждение для установки выражения, потому что из ?expression:

В качестве объекта режима "выражение" есть список...

Ну, это не "список" в обычном смысле; это абстрактное синтаксическое дерево, которое является списком.


Второй запрос

Какая польза от a = list(foo = 1, bar = 2)?

В этом нет никакого преимущества. Вы должны были уже прочитать в другом месте, что растущие объекты - это плохая практика в R. Случайный поиск в Google дает: растущие объекты и предварительное выделение памяти цикла.

Если вы знаете длину вектора, а также значение его каждого элемента, создайте его непосредственно, как a = list(foo = 1, bar = 2).

Если вы знаете длину вектора, но его значения элементов должны вычисляться (скажем, по петле), установите вектор и заполните его, как a <- vector("list", 2); a[[1]] <- 1; a[[2]] <- 2; names(a) <- c("foo", "bar") a <- vector("list", 2); a[[1]] <- 1; a[[2]] <- 2; names(a) <- c("foo", "bar") a <- vector("list", 2); a[[1]] <- 1; a[[2]] <- 2; names(a) <- c("foo", "bar").


В ответ на Tjebo

Я действительно искал ?mode, но он не перечисляет возможные режимы. Он указывает на ?typeof который затем указывает на возможные значения, перечисленные в структуре TypeTable в src/main/util.c Мне не удалось найти этот файл даже в папке (OSX). Любая идея, где это найти?

Это означает источник R-дистрибутива, который является файлом ".tar.gz" на CRAN. Альтернативой является поиск https://github.com/wch/r-source. В любом случае, это таблица:

TypeTable[] = {
    { "NULL",       NILSXP     },  /* real types */
    { "symbol",     SYMSXP     },
    { "pairlist",   LISTSXP    },
    { "closure",    CLOSXP     },
    { "environment",    ENVSXP     },
    { "promise",    PROMSXP    },
    { "language",   LANGSXP    },
    { "special",    SPECIALSXP },
    { "builtin",    BUILTINSXP },
    { "char",       CHARSXP    },
    { "logical",    LGLSXP     },
    { "integer",    INTSXP     },
    { "double",     REALSXP    }, /*-  "real", for R <= 0.61.x */
    { "complex",    CPLXSXP    },
    { "character",  STRSXP     },
    { "...",        DOTSXP     },
    { "any",        ANYSXP     },
    { "expression", EXPRSXP    },
    { "list",       VECSXP     },
    { "externalptr",    EXTPTRSXP  },
    { "bytecode",   BCODESXP   },
    { "weakref",    WEAKREFSXP },
    { "raw",        RAWSXP },
    { "S4",     S4SXP },
    /* aliases : */
    { "numeric",    REALSXP    },
    { "name",       SYMSXP     },

    { (char *)NULL, -1     }
};

Ответ 2

Документация Per R на фигурных скобках и скобках (тип ?'{' Для их чтения), скобки возвращают последнее выражение, оцениваемое внутри них.

В этом случае a <- {} существу "возвращает" нулевой объект и поэтому эквивалентен a <- NULL, который устанавливает пустую переменную, которая затем может рассматриваться как список.

Кстати, именно поэтому можно писать R-функции, в которых выход функции возвращается просто путем записи имени возвращаемой переменной в качестве окончательной команды функции. Например:

function(x) {
    y <- x * 2
    return(y)
}

Эквивалентно:

function(x) {
    y <- x * 2
    y
}

Или даже:

function(x) {
    y <- x * 2
}

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