
Hola habrozhiteli! Muchos usuarios usan R para tareas específicas: aquí para construir un histograma, allí para realizar un análisis de regresión o realizar otras operaciones separadas relacionadas con el procesamiento de datos estadísticos. Pero este libro está escrito para aquellos que desean desarrollar software en R. Las habilidades de programación de los lectores previstos de este libro pueden variar desde una calificación profesional hasta "Tomé un curso de programación en la universidad", pero la clave es escribir el código R para propósitos específicos. . (Generalmente no es necesario un conocimiento profundo de las estadísticas).
Algunos ejemplos de lectores que podrían beneficiarse de este libro:
- Un analista (por ejemplo, trabajando en un hospital o agencia gubernamental) que tiene que emitir informes estadísticos regularmente y desarrollar programas para este propósito.
- Un científico involucrado en el desarrollo de una metodología estadística: nuevos o integrando métodos existentes en procedimientos integrados. La metodología necesita ser codificada para que pueda ser utilizada en la comunidad de investigación.
- Especialistas en marketing, soporte legal, periodismo, publicaciones, etc., involucrados en el desarrollo de código para construir representaciones gráficas complejas de datos.
- Programadores profesionales con experiencia en desarrollo de software asignados a proyectos relacionados con el análisis estadístico.
- Estudiantes que estudian estadísticas y procesamiento de datos.
Por lo tanto, este libro no es una referencia a los innumerables métodos estadísticos del notable paquete R. De hecho, está dedicado a la programación y aborda problemas de programación que rara vez se encuentran en otros libros sobre R. Incluso los temas fundamentales se consideran desde el ángulo de la programación. Algunos ejemplos de este enfoque:
- Este libro contiene secciones de los "Ejemplos avanzados". Por lo general, proporcionan funciones completas de propósito general en lugar de fragmentos de código aislados basados en datos específicos. Además, algunas de estas funciones pueden ser útiles en su trabajo diario con R. Al estudiar estos ejemplos, no solo aprenderá cómo funcionan las construcciones R específicas, sino también cómo combinarlas en programas útiles. En muchos casos, proporciono descripciones de soluciones alternativas y respondo la pregunta: "¿Por qué se hizo esto de esta manera?"
- El material se presenta teniendo en cuenta la percepción del programador. Por ejemplo, al describir marcos de datos, no solo afirmo que el marco de datos en R es una lista, sino que también señalo las consecuencias de este hecho desde el punto de vista de la programación. También en el texto, R se compara con otros idiomas donde esto puede ser útil (para lectores que hablan estos idiomas).
- La depuración juega un papel crucial en la programación en cualquier lenguaje, pero la mayoría de los libros sobre R no mencionan este tema. En este libro, dediqué un capítulo entero a las herramientas de depuración, utilicé el principio de "ejemplos avanzados" y presenté demostraciones completamente desarrolladas de cómo los programas se depuran en realidad.
- Hoy en día, las computadoras multi-core han aparecido en todos los hogares, y la programación de procesadores gráficos (GPU) está produciendo una revolución imperceptible en el campo de la computación científica. Cada vez más aplicaciones R requieren grandes cantidades de cómputo, y el procesamiento paralelo se ha vuelto relevante para los programadores R. Se dedica un capítulo completo a este tema en el libro, además de describir la mecánica, también se dan ejemplos avanzados.
- Un capítulo separado habla sobre cómo usar la información sobre la implementación interna y otros aspectos de R para acelerar el trabajo del código R.
- Uno de los capítulos se centra en la interfaz de R con otros lenguajes de programación como C y Python. Una vez más, se presta especial atención a ejemplos avanzados y recomendaciones para la depuración.
Extracto 7.8.4. ¿Cuándo deben usarse las variables globales?
No hay consenso sobre el uso de variables globales en la comunidad de programadores. Obviamente, no hay una respuesta correcta a la pregunta planteada en el título de esta sección, ya que se trata de preferencias personales y estilo. Sin embargo, muchos programadores creen que una prohibición completa de las variables globales, que muchos profesores de programación recomiendan, sería innecesariamente difícil. En esta sección, examinamos los posibles beneficios de las variables globales en el contexto de las estructuras de R. El término "variable global" significará cualquier variable que se encuentre en la jerarquía del entorno por encima del nivel de código de interés.
Usar variables globales en R es más común de lo que cabría esperar. Sorprendentemente, R usa variables globales muy ampliamente en su implementación interna (tanto en código C como en funciones R). Por lo tanto, el operador de superasignación << - se usa en muchas funciones de biblioteca de R (aunque generalmente se usa para escribir en una variable ubicada solo un nivel más arriba en la jerarquía de variables). El código multiproceso y el código GPU utilizados para escribir programas rápidos (consulte el capítulo 16) generalmente usan variables globales que proporcionan el mecanismo principal de interacción entre los ejecutores paralelos.
Ahora, para concretar, volvamos a un ejemplo anterior de la sección 7.7:
f <- function(lxxyy) { # lxxyy — , x y ... lxxyy$x <- ... lxxyy$y <- ... return(lxxyy) } # x y lxy$x <- ... lxy$y <- ... lxy <- f(lxy) # x y ... <- lxy$x ... <- lxy$y
Como se mencionó anteriormente, este código puede volverse engorroso, especialmente si xey son listas en sí mismas.
Por otro lado, eche un vistazo a un esquema alternativo utilizando variables globales:
f <- function() { ... x <<- ... y <<- ... } # x y x <-... y <-... f() # x y # x y ... <- x ... <- y
Quizás la segunda versión es mucho más limpia, menos voluminosa y no requiere manipulación de la lista. El código claro generalmente crea menos problemas de escritura, depuración y mantenimiento.
Por estas razones, para simplificar y reducir el volumen del código, decidimos usar variables globales en lugar de devolver listas en el código DES proporcionado anteriormente. Considere este ejemplo con más detalle.
Se utilizaron dos variables globales (ambas son listas que contienen información diferente): la variable sim está asociada con el código de la biblioteca y la variable mm1glbls está asociada con el código de aplicación específico M / M / 1. Comencemos con sim.
Incluso los programadores que están restringidos acerca de las variables globales están de acuerdo en que el uso de tales variables puede justificarse si son realmente globales, en el sentido de que se usan ampliamente en el programa. Todo esto se relaciona con la variable sim del ejemplo DES: se usa tanto en el código de la biblioteca (en schedevnt (), getnextevnt () y dosim ()) como en el código M / M / 1 (en mm1reactevnt ()). En este ejemplo particular, las llamadas posteriores a sim se limitan a la lectura, pero la grabación es posible en algunas situaciones. Un ejemplo típico de este tipo es una posible implementación de cancelación de eventos. Por ejemplo, tal situación puede ocurrir cuando se modela el principio "anterior de los dos": se planifican dos eventos, y cuando uno de ellos ocurre, el otro debe cancelarse.
Por lo tanto, usar sim como variable global parece justificado. Sin embargo, si nos negamos decididamente a usar variables globales, sim podría colocarse en una variable local dentro de dosim (). Esta función pasará sim en el argumento de todas las funciones mencionadas en el párrafo anterior (schedevnt (), getnextevnt (), etc.), y cada una de estas funciones devolverá una variable sim modificada.
Por ejemplo, línea 94:
reactevnt(head)
convertido a la siguiente forma:
sim <- reactevnt(head)
Después de eso, se debe agregar la siguiente línea a la función mm1reactevnt () asociada con una aplicación específica:
return(sim)
Puede hacer algo similar con mm1glbls al incluir en dosim () una variable local con el nombre (por ejemplo) appvars. Pero si esto se hace con dos variables, entonces deben colocarse en la lista para que ambas variables puedan ser devueltas desde la función, como en el ejemplo anterior de la función f (). Y luego surge la estructura voluminosa de las listas dentro de las listas, que se mencionó anteriormente, o más bien, las listas dentro de las listas dentro de las listas.
Por otro lado, los opositores al uso de variables globales notan que la simplicidad del código no es en vano. Les preocupa que durante el proceso de depuración haya dificultades para encontrar lugares donde la variable global cambie el valor, ya que el cambio puede ocurrir en cualquier parte del programa. Parece que en el mundo de los editores de texto modernos y las herramientas de desarrollo integradas que ayudarán a encontrar todas las ocurrencias de una variable, el problema se va por el camino (¡el artículo original que insta a abandonar el uso de variables globales se publicó en 1970!). Sin embargo, este factor debe tenerse en cuenta.
Otro problema que los críticos mencionan se encuentra cuando se llama a una función desde varias partes no relacionadas de un programa con valores diferentes. Por ejemplo, imagine que la función f () se llama desde diferentes partes del programa, y cada llamada recibe sus propios valores x e y en lugar de un valor para cada uno. El problema puede resolverse creando vectores de valores x e y, en los que cada instancia de f () en su programa corresponde a un elemento separado. Sin embargo, esto perderá la simplicidad de usar variables globales.
Estos problemas se encuentran no solo en R, sino también en un contexto más general. Sin embargo, en R el uso de variables globales en el nivel superior crea un problema adicional, ya que el usuario en este nivel generalmente tiene muchas variables. Existe el peligro de que el código que usa variables globales pueda reemplazar accidentalmente una variable completamente extraña con el mismo nombre.
Por supuesto, el problema se resuelve fácilmente: es suficiente elegir nombres largos para las variables globales que están vinculadas a una aplicación específica. Sin embargo, los entornos también proporcionan un compromiso razonable, como en la siguiente situación para el ejemplo DES.
Dentro de la función dosim (), la línea
sim <<- list()
puede ser reemplazado con una cadena
assign("simenv",new.env(),envir=.GlobalEnv)
Crea un nuevo entorno al que hace referencia la variable simenv en el nivel superior. Este entorno sirve como contenedor para encapsular variables globales a las que se puede acceder mediante llamadas para obtener () y asignar (). Por ejemplo, cadenas
if (is.null(sim$evnts)) { sim$evnts <<- newevnt
en schedevnt () toma la forma
if (is.null(get("evnts",envir=simenv))) { assign("evnts",newevnt,envir=simenv)
Sí, esta solución también es engorrosa, pero al menos no es tan complicada como las listas dentro de las listas dentro de las listas. Y protege contra la escritura accidental en una variable extraña en el nivel superior. El uso del operador de superasignación todavía proporciona un código menos engorroso, pero esta compensación debe tenerse en cuenta.
Como de costumbre, no existe un estilo de programación único que proporcione los mejores resultados en todas las situaciones. Una solución con variables globales es otra opción que debe incluirse en su arsenal de herramientas de programación.
7.8.5. Cortocircuitos
Permítame recordarle que los cierres de R consisten en argumentos y el cuerpo de la función junto con el entorno en el momento de la llamada. El hecho de que el entorno esté incluido está involucrado en el paradigma de programación, que utiliza el concepto, también llamado cierre (aquí hay cierta sobrecarga de terminología).
Un cierre es una función que crea una variable local, y luego crea otra función que accede a esta variable. La descripción es demasiado abstracta, así que mejor daré un ejemplo.
1 > counter 2 function () { 3 ctr <- 0 4 f <- function() { 5 ctr <<- ctr + 1 6 cat("this count currently has value",ctr,"\n") 7 } 8 return(f) 9 }
Veamos cómo funciona este código antes de sumergirnos en los detalles de implementación:
> c1 <- counter() > c2 <- counter() > c1 function() { ctr <<- ctr + 1 cat("this count currently has value",ctr,"\n") } <environment: 0x8d445c0> > c2 function() { ctr <<- ctr + 1 cat("this count currently has value",ctr,"\n") } <environment: 0x8d447d4> > c1() this count currently has value 1 > c1() this count currently has value 2 > c2() this count currently has value 1 > c2() this count currently has value 2 > c2() this count currently has value 3 > c1() this count currently has value 3
Aquí, la función counter () se llama dos veces, y los resultados se asignan c1 y c2. Como se esperaba, estas dos variables consisten en funciones, a saber, copias de f (). Sin embargo, f () accede a la variable ctr a través del operador de superasignación, y esta variable será una variable con el nombre local local para counter (), ya que será la primera en la ruta en la jerarquía del entorno. Es parte del entorno f (), y como tal está empaquetado en lo que regresa al lado de la llamada del contador (). El punto clave es que con diferentes llamadas a counter (), la variable ctr estará en diferentes entornos (en el entorno de ejemplo, se almacenó en la memoria en las direcciones 0x8d445c0 y 0x8d447d4). En otras palabras, diferentes llamadas a counter () crearán instancias físicamente diferentes de ctr.
Como resultado, las funciones c1 () y c2 () funcionan como contadores completamente independientes. Esto se puede ver en el ejemplo donde cada función se llama varias veces.
»Se puede encontrar más información sobre el libro en
el sitio web del editor»
Contenidos»
ExtractoCupón de 25% de descuento para vendedores ambulantes -
RTras el pago de la versión en papel del libro, se envía una versión electrónica del libro por correo electrónico.