2015-05-31 11 views
10

Hier ist der Code:Was ist "{" Klasse 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)}) 

Ausgang:

[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) 
} 

Offenbar das Ergebnis substitute(expr) etwas von der Klasse produziert "{". Was ist diese Klasse genau? Warum ist {matrix(NA, 4, 4)} der Länge 2? Was bedeuten diese seltsamen Ankömmlinge?

Antwort

10

Die { ist die Klasse für einen Codeblock. Gerade in den Klassen suchen, beachten Sie den Unterschied zwischen diesen

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

Eine Klasse von { mehrere Anweisungen halten kann. Die length() gibt an, wie viele Anweisungen sich im Block befinden (einschließlich des Beginns des Blocks). Zum Beispiel

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

Die Attribute „srcref“ und „srcfile“ sind, wie R-Spuren, wo Funktionen definiert werden für den Versuch, informative Fehlermeldungen zu geben. Sie können die Hilfeseite ?srcfile für weitere Informationen darüber sehen.

5

Was Sie sehen, ist eine Reflektion der Art und Weise, wie R seine interne Sprachstruktur durch seine eigenen Datenstrukturen darstellt.

Die Funktion substitute() gibt den Syntaxbaum eines R-Ausdrucks zurück. Der Syntaxbaum ist ein Baum von Sprachelementen. Diese können Literalwerte, Symbole (im Grunde Variablennamen), Funktionsaufrufe und versteifte Blöcke enthalten. Hier ist eine Demonstration aller R-Sprachelemente, wie durch substitute() zurückgegeben, zeigt ihre Typen in allen R-Typ Klassifikationsschemata:

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"  "{" 

Hinweise:

  • Hinweis, wie alle drei Typen Klassifikationsschemata sehr ähnlich sind , aber subtil anders. Dies kann eine Quelle der Verwirrung sein. typeof() gibt den Speichertyp des Objekts, manchmal auch als "interner" Typ (um ehrlich zu sein, sollte es wahrscheinlich nicht "internal" genannt werden, da häufig sehr direkt dem Benutzer auf der R-Ebene ausgesetzt ist, aber es wird oft so beschrieben, ich würde es den "fundamentalen" oder "zugrunde liegenden" Typ nennen, mode() gibt ein ähnliches Klassifizierungsschema, das jeder wahrscheinlich ignorieren sollte, und class() gibt das implizite (wenn es kein class Attribut gibt) oder explizites (wenn dort ist) Klasse des Objekts, das für die S3-Methoden-Suche verwendet wird (und, sollte gesagt werden, wird manchmal direkt von R-Code untersucht, unabhängig vom S3-Lookup-Prozess).
  • Beachten Sie, wie TRUE eine logische wörtliche, aber T ist ein Symbol, wie jede andere Variablennamen, und nur zufällig auf TRUE vorbelegt werden (und dito für F und FALSE). Aus diesem Grunde manchmal Leute empfehlen gegen die Verwendung von T und F für den Einsatz von TRUE und FALSE, weil T und F können (aber ich persönlich lieber T und F für die Zerschneidung benutzen, niemand sollte jemals neu zuzuweisen diejenigen!) Neu zugewiesen werden.
  • Der aufmerksame Leser wird bemerken, dass ich in meiner Demonstration von Literalen den rohen Typ weggelassen habe. Dies liegt daran, dass es in R kein rohes Literal gibt.In der Tat gibt es sehr wenige Möglichkeiten, Rohvektoren in R zu finden; raw(), as.raw(), charToRaw() und rawConnectionValue() sind die einzigen Möglichkeiten, dass mir bewusst bin, und wenn ich diese Funktionen in einem substitute() Aufruf verwendet, würden sie als "call" Objekte zurückgegeben werden, wie gerade im sum(somevar) Beispiel nicht wörtlich Rohwerte. Das Gleiche gilt für den Listentyp; Es gibt kein Listenliteral (obwohl es viele Möglichkeiten gibt, eine Liste über Funktionsaufrufe zu erhalten). Plain-Rohvektoren geben 'raw' für alle drei Typenklassifikationen zurück, und einfache Listen geben 'list' für alle drei Typenklassifikationen zurück.

Nun, wenn Sie einen Parse-Baum, die komplizierter als ein einfaches Literalwert oder Symbol ist (dh es muss ein Funktionsaufruf oder verspannt Ausdruck sein), können Sie den Inhalt des Parse-Baums im Allgemeinen untersuchen kann durch Nötigung auflisten. So stellt R seine interne Sprachstruktur durch eigene Datenstrukturen zur Verfügung.

Tauchen in Ihrem Beispiel:

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

Dies macht deutlich, warum length() kehrt 2: das ist die Länge der Liste, die den Parsing-Baum darstellt. Im Allgemeinen wird die Verspannung des Ausdrucks in der ersten Liste Komponente übersetzt, und die verbleibenden Listenkomponenten werden aus den durch Semikolon getrennte Aussagen innerhalb der geschweiften Klammern gebaut:

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 

Beachten Sie, dass diese identisch ist, wie Funktionsaufrufe Arbeit , jedoch mit dem Unterschied, dass für die Funktionsaufrufe, die Listenkomponenten aus den durch Kommata getrennte Argumente an den Funktionsaufruf gebildet werden:

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 

aus dem obigen wird deutlich, dass die erste Liste Komponente tatsächlich ein Symbol darstellt, der Name einer Funktion, sowohl für Klammerausdrücke als auch für Funktionsaufrufe. Mit anderen Worten, die offene Klammer ist ein Funktionsaufruf, der einfach sein letztes Argument zurückgibt. So wie eckige Klammern normale Funktion ruft eine komfortable Syntax gebaut auf sie sind, ist die offene Klammer ein normaler Funktionsaufruf mit einer praktischen Syntax oben drauf gebaut:

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

zu Ihrem Beispiel zurückkehrend, können wir Untersuchen Sie den Syntaxbaum vollständig, indem Sie die Listenstruktur durchlaufen, die den Syntaxbaum darstellt. Ich schrieb eine nette kleine rekursive Funktion, die dies sehr leicht tun können:

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 

Wie Sie sehen können, die verspannt Ausdruck verwandelt sich in eine normale Funktion Aufruf der Funktion `{`(), ein Argument nehmen, die die einzige Aussage Du hast es codiert. Diese Anweisung besteht aus einem einzelnen Funktionsaufruf an matrix(), wobei drei Argumente genommen werden, von denen jeder ein Literalwert ist: NA, 4 und 4. Und das ist der gesamte Syntaxbaum.

So jetzt können wir die Bedeutung der "{" Klasse auf einer tiefen Ebene verstehen: Es stellt ein Element eines Parse-Baums, der ein Funktionsaufruf an die `{`()-Funktion ist. Es wird zufällig anders klassifiziert als andere Funktionsaufrufe ("{" statt "call"), aber soweit ich das beurteilen kann, hat das nirgendwo eine Bedeutung. Beachten Sie auch, dass die typeof() und mode() ("language" bzw. "call") zwischen allen Parse Tree-Elementen identisch sind, die Funktionsaufrufe repräsentieren, sowohl für `{`() als auch für andere.

+0

Sie können einfach 'quote' in allen Beispielen verwenden, zum Beispiel' substitute ({matrix (NA, 4, 4)}) == quote ({matrix (NA, 4, 4)}) ' – qed