2015-05-31 7 views
10

Вот код:Что такое "{" class in R?

mf = function(..., expr) { 
    expr = substitute(expr) 
    print(class(expr)) 
    print(str(expr)) 
    expr 
} 
mf(a = 1, b = 2, expr = {matrix(NA, 4, 4)}) 

Выход:

[1] "{" 
length 2 { matrix(NA, 4, 4) } 
- attr(*, "srcref")=List of 2 
    ..$ :Class 'srcref' atomic [1:8] 1 25 1 25 25 25 1 1 
    .. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860> 
    ..$ :Class 'srcref' atomic [1:8] 1 26 1 41 26 41 1 1 
    .. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860> 
- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860> 
- attr(*, "wholeSrcref")=Class 'srcref' atomic [1:8] 1 0 1 42 0 42 1 1 
    .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860> 
NULL 
{ 
matrix(NA, 4, 4) 
} 

Видимо результат substitute(expr) производит что-то класса "{". Что это за класс? Почему {matrix(NA, 4, 4)} длины 2? Что означают эти странные аттракционы?

ответ

10

{ - это класс для блока кода. Просто глядя на классы, обратите внимание на разницу между этими

mf(a = 1, b = 2, expr = {matrix(NA, 4, 4)}) 
# [1] "{" 
mf(a = 1, b = 2, expr = matrix(NA, 4, 4)) 
# [1] "call" 

Класс { может содержать несколько операторов. length() указывает, сколько операторов находится в блоке (включая начало блока). Например

length(quote({matrix(NA, 4, 4)})) 
# [1] 2 
length(quote({matrix(NA, 4, 4); matrix(NA,3,3)})) 
# [1] 3 
length(quote({})) 
# [1] 1 

Атрибуты «srcref» и «srcfile» являются как R отслеживает, где функции определены для пытается дать информативные сообщения об ошибках. Дополнительную информацию об этом можно найти на странице справки ?srcfile.

5

То, что вы видите, является отражением того, как R предоставляет свою внутреннюю структуру языка через свои собственные структуры данных.

Функция substitute() возвращает дерево разбора выражения R. Дерево разбора - это дерево элементов языка. Они могут включать в себя литеральные значения, символы (в основном имена переменных), вызовы функций и блокированные блоки. Вот демонстрация всех R элементов языка в качестве возвращаемого substitute(), с указанием их типов во всех классификационном типе схем АиРа:

tmc <- function(x) c(typeof(x),mode(x),class(x)); 
tmc(substitute(TRUE)); 
## [1] "logical" "logical" "logical" 
tmc(substitute(4e5L)); 
## [1] "integer" "numeric" "integer" 
tmc(substitute(4e5)); 
## [1] "double" "numeric" "numeric" 
tmc(substitute(4e5i)); 
## [1] "complex" "complex" "complex" 
tmc(substitute('a')); 
## [1] "character" "character" "character" 
tmc(substitute(somevar)); 
## [1] "symbol" "name" "name" 
tmc(substitute(T)); 
## [1] "symbol" "name" "name" 
tmc(substitute(sum(somevar))); 
## [1] "language" "call"  "call" 
tmc(substitute(somevec[1])); 
## [1] "language" "call"  "call" 
tmc(substitute(somelist[[1]])); 
## [1] "language" "call"  "call" 
tmc(substitute(somelist$x)); 
## [1] "language" "call"  "call" 
tmc(substitute({blah})); 
## [1] "language" "call"  "{" 

Примечание:

  • Обратите внимание, как все схемы классификации три типа очень похожи , но тонко отличается. Это может быть источником путаницы. typeof() дает тип хранилища объекта, иногда называемый «внутренним» типом (честно говоря, его, вероятно, не следует называть «внутренним», потому что он равен, который часто отображается непосредственно пользователю на уровне R, но он часто описывается таким образом, я бы назвал его «фундаментальным» или «базовым» типом), mode() дает аналогичную схему классификации, которую каждый должен, вероятно, игнорировать, а class() дает неявный (если нет атрибута class) или явный (если есть is) класс объекта, который используется для поиска метода S3 (и, надо сказать, иногда проверяется непосредственно кодом R, независимо от процесса поиска S3).
  • Обратите внимание, как TRUE является логическим буквальным, но T является символом, так же как и любого другого переменным имя, и только случается быть назначен TRUE по умолчанию (и повторам F и FALSE). Вот почему иногда люди рекомендуют против использования T и F в пользу использования TRUE и FALSE, потому что T и F могут быть переназначены (но лично я предпочитаю использовать T и F для краткости, никто никогда не должен переназначить те!).
  • Проницательный читатель заметит, что в моей демонстрации литералов я опустил необработанный тип. Это связано с тем, что в R.На самом деле, существует очень мало способов захвата необработанных векторов в R; raw(), as.raw(), charToRaw() и rawConnectionValue() являются единственными способами, которые я знаю, и если бы я использовал эти функции в substitute() вызова, они будут возвращены в качестве "call" объектов, так же, как в примере sum(somevar), не буквальное сырье значений. То же самое можно сказать и о типе списка; нет такой вещи, как литерал списка (хотя есть много способов получить список через вызовы функций). Обычные необработанные векторы возвращают 'raw' для всех трех типов классификаций, а простые списки возвращают 'list' для всех трех типов классификаций.

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

Погружение в вашем примере:

pt <- as.list(substitute({matrix(NA,4,4)})); 
pt; 
## [[1]] 
## `{` 
## 
## [[2]] 
## matrix(NA, 4, 4) 

Это делает понятным, почему length() возвращается 2: это длина списка, который представляет дерево синтаксического разбора. В общем, распорка выражения переводится в первый компонент списка, а остальные компоненты списка построены из точки с запятой заявлений в фигурных скобках:

as.list(substitute({})); 
## [[1]] 
## `{` 
## 
as.list(substitute({a})); 
## [[1]] 
## `{` 
## 
## [[2]] 
## a 
## 
as.list(substitute({a;b})); 
## [[1]] 
## `{` 
## 
## [[2]] 
## a 
## 
## [[3]] 
## b 
## 
as.list(substitute({a;b;c})); 
## [[1]] 
## `{` 
## 
## [[2]] 
## a 
## 
## [[3]] 
## b 
## 
## [[4]] 
## c 

Обратите внимание, что это идентично тому, как вызовы функций работы , за исключением того, с той разницей, что для вызовов функций, компоненты списка формируются из разделенных запятыми аргументов при вызове функции:

as.list(substitute(sum())); 
## [[1]] 
## sum 
## 
as.list(substitute(sum(1))); 
## [[1]] 
## sum 
## 
## [[2]] 
## [1] 1 
## 
as.list(substitute(sum(1,3))); 
## [[1]] 
## sum 
## 
## [[2]] 
## [1] 1 
## 
## [[3]] 
## [1] 3 
## 
as.list(substitute(sum(1,3,5))); 
## [[1]] 
## sum 
## 
## [[2]] 
## [1] 1 
## 
## [[3]] 
## [1] 3 
## 
## [[4]] 
## [1] 5 

из сказанного выше становится ясно, что первый компонент списка на самом деле является символ, представляющий имя функции, как для скопированных выражений, так и для вызовов функций. Другими словами, открытая скобка - это вызов функции, который просто возвращает свой последний аргумент. Так же, как квадратные скобки нормальные вызовы функций с помощью удобного синтаксиса, построенного на них сверху, открытая скобка нормальный вызов функции с помощью удобного синтаксиса, построенного на нем:

a <- 4:6; 
a[2]; 
## [1] 5 
`[`(a,2); 
## [1] 5 
{1;2}; 
## [1] 2 
`{`(1,2); 
## [1] 2 

Возвращаясь к вашему примеру, мы можем полностью изучить дерево разбора, пройдя по структуре списка, которая представляет дерево разбора. Я просто написал миленький рекурсивную функцию, которая может сделать это очень легко:

unwrap <- function(x) if (typeof(x) == 'language') lapply(as.list(x),unwrap) else x; 
unwrap(substitute(3)); 
## [1] 3 
unwrap(substitute(a)); 
## a 
unwrap(substitute(a+3)); 
## [[1]] 
## `+` 
## 
## [[2]] 
## a 
## 
## [[3]] 
## [1] 3 
## 
unwrap(substitute({matrix(NA,4,4)})); 
## [[1]] 
## `{` 
## 
## [[2]] 
## [[2]][[1]] 
## matrix 
## 
## [[2]][[2]] 
## [1] NA 
## 
## [[2]][[3]] 
## [1] 4 
## 
## [[2]][[4]] 
## [1] 4 

Как вы можете видеть, приготовился выражение превращается в нормальную функцию вызова функции `{`(), принимая один аргумент, который является один оператор вы закодировали его. Этот оператор состоит из одного вызова функции для matrix() с тремя аргументами, каждое из которых является буквальным значением: NA, 4 и 4. И это все дерево синтаксического анализа.

Итак, теперь мы можем понять значение класса "{" на глубоком уровне: он представляет собой элемент дерева синтаксического анализа, который является вызовом функции функции `{`(). Это происходит иначе, чем другие вызовы функций ("{" вместо "call"), но насколько я могу судить, это нигде не имеет значения. Также обратите внимание, что typeof() и mode() идентичны ("language" и "call" соответственно) между всеми элементами дерева разбора, представляющими вызовы функций, как для `{`(), так и для других.

+0

Вы можете просто использовать 'quote' во всех примерах, поскольку, например,' substitute ({matrix (NA, 4, 4)}) == quote ({matrix (NA, 4, 4)}) ' – qed