12 Tipos básicos
12.1 Introducción
Para hablar de objetos y programación orientada a objetos en R, primero debemos aclarar una confusión fundamental sobre dos usos de la palabra “objeto”. Hasta ahora en este libro, hemos usado la palabra en el sentido general captado por la concisa cita de John Chambers: “Todo lo que existe en R es un objeto”. Sin embargo, aunque todo es un objeto, no todo está orientado a objetos. Esta confusión surge porque los objetos base provienen de S y se desarrollaron antes de que nadie pensara que S podría necesitar un sistema OOP. Las herramientas y la nomenclatura evolucionaron orgánicamente durante muchos años sin un solo principio rector.
La mayoría de las veces, la distinción entre objetos y objetos orientados a objetos no es importante. Pero aquí tenemos que entrar en detalles esenciales, así que usaremos los términos objetos base y objetos OO para distinguirlos.
Estructura
La Sección 12.2 le muestra cómo identificar objetos base y OO.
La Sección 12.3 proporciona un conjunto completo de los tipos base utilizados para construir todos los objetos.
12.2 Base versus objetos OO
Para saber la diferencia entre un objeto base y OO, usa is.object()
o sloop::otype()
:
# Un objeto básico:
is.object(1:10)
#> [1] FALSE
::otype(1:10)
sloop#> [1] "base"
# Un objeto OO
is.object(mtcars)
#> [1] TRUE
::otype(mtcars)
sloop#> [1] "S3"
Técnicamente, la diferencia entre los objetos base y OO es que los objetos OO tienen un atributo de “clase”:
attr(1:10, "class")
#> NULL
attr(mtcars, "class")
#> [1] "data.frame"
Puede que ya estés familiarizado con la función class()
. Es seguro aplicar esta función a objetos S3 y S4, pero devuelve resultados engañosos cuando se aplica a objetos base. Es más seguro usar sloop::s3_class()
, que devuelve la clase implícita que los sistemas S3 y S4 usarán para seleccionar métodos. Aprenderá más sobre s3_class()
en la Sección 13.7.1.
<- matrix(1:4, nrow = 2)
x class(x)
#> [1] "matrix" "array"
::s3_class(x)
sloop#> [1] "matrix" "integer" "numeric"
12.3 Tipos básicos
Mientras que solo los objetos OO tienen un atributo de clase, cada objeto tiene un tipo base:
typeof(1:10)
#> [1] "integer"
typeof(mtcars)
#> [1] "list"
Los tipos base no forman un sistema OOP porque las funciones que se comportan de manera diferente para diferentes tipos base se escriben principalmente en código C que usa instrucciones de cambio. Esto significa que solo R-core puede crear nuevos tipos, y crear un nuevo tipo es mucho trabajo porque cada declaración de cambio debe modificarse para manejar un nuevo caso. Como consecuencia, rara vez se agregan nuevos tipos base. El cambio más reciente, en 2011, agregó dos tipos exóticos que nunca se ven en R, pero que son necesarios para diagnosticar problemas de memoria. Antes de eso, el último tipo agregado fue un tipo base especial para objetos S4 agregado en 2005.
En total, hay 25 tipos de base diferentes. Se enumeran a continuación, agrupados libremente según el lugar en el que se analicen en este libro. Estos tipos son los más importantes en el código C, por lo que a menudo los verá llamados por sus nombres de tipo C. Los he incluido entre paréntesis.
Vectores, Capítulo 3, incluye tipos
NULL
(NILSXP
),logical
(LGLSXP
),integer
(INTSXP
),double
(REALSXP
),complex
(CPLXSXP
),character
(STRSXP
),list
(VECSXP
), yraw
(RAWSXP
).typeof(NULL) #> [1] "NULL" typeof(1L) #> [1] "integer" typeof(1i) #> [1] "complex"
Las funciones, Capítulo 6, incluyen los tipos
cierre
(funciones regulares de R,CLOSXP
),especiales
(funciones internas,SPECIALSXP
) eincorporadas
(funciones primitivas,BUILTINSXP
).typeof(mean) #> [1] "closure" typeof(`[`) #> [1] "special" typeof(sum) #> [1] "builtin"
Las funciones internas y primitivas se describen en la Sección 6.2.2.
Entornos, Capítulo 7, tienen tipo
entorno
(ENVSXP
).typeof(globalenv()) #> [1] "environment"
El tipo
S4
(S4SXP
), Capítulo 15, se usa para las clases de S4 que no heredan de un tipo base existente.<- stats4::mle(function(x = 1) (x - 2) ^ 2) mle_obj typeof(mle_obj) #> [1] "S4"
Los componentes del lenguaje, Capítulo 18), incluyen
símbolo
(también conocido como nombre,SYMSXP
),idioma
(generalmente llamadas llamadas,LANGSXP
) ypairlist
(usado para argumentos de función,LISTSXP
) tipos.typeof(quote(a)) #> [1] "symbol" typeof(quote(a + 1)) #> [1] "language" typeof(formals(mean)) #> [1] "pairlist"
expression
(EXPRSXP
) es un tipo de propósito especial que solo es devuelto porparse()
yexpression()
. Las expresiones generalmente no son necesarias en el código de usuario.Los tipos restantes son esotéricos y rara vez se ven en R. Son importantes principalmente para el código C:
externalptr
(EXTPTRSXP
),weakref
(WEAKREFSXP
),bytecode
(BCODESXP
),promise
(PROMSXP
),...
(DOTSXP
), yany
(ANYSXP
).
Es posible que hayas oído hablar de mode()
y storage.mode()
. No utilice estas funciones: solo existen para proporcionar nombres de tipo que sean compatibles con S.
12.3.1 Tipo numérico
Tenga cuidado al hablar del tipo numérico, porque R usa “numérico” para referirse a tres cosas ligeramente diferentes:
En algunos lugares, numérico se usa como un alias para el tipo doble. Por ejemplo,
as.numeric()
es idéntico aas.double()
, ynumeric()
es idéntico adouble()
.(R también usa ocasionalmente real en lugar de doble;
NA_real_
es el único lugar donde es probable que encuentres esto en la práctica.)En los sistemas S3 y S4, numérico se usa como una forma abreviada de tipo entero o doble, y se usa cuando se seleccionan métodos:
::s3_class(1) sloop#> [1] "double" "numeric" ::s3_class(1L) sloop#> [1] "integer" "numeric"
is.numeric()
pruebas para objetos que se comportan como números. Por ejemplo, los factores tienen el tipo “entero” pero no se comportan como números (es decir, no tiene sentido tomar la media del factor).typeof(factor("x")) #> [1] "integer" is.numeric(factor("x")) #> [1] FALSE
En este libro, siempre uso numérico para indicar un objeto de tipo entero o doble.