ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.04.2021

Просмотров: 907

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

Circle 6

Doing Global Assignments

Heretics imprisoned in flaming tombs inhabit Circle 6.

A global assignment can be performed with

8

<<-

8

:

>

x <- 1

>

y <- 2

>

fun

function ()

{

x <- 101
y <<- 102

}

>

fun()

>

x

[1] 1
>

y

[1] 102

This is life beside a volcano.

If you think you need

8

<<-

8

, think again. If on reflection you still think you

need

8

<<-

8

, think again. Only when your boss turns

red

with anger over you

not doing anything should you temporarily give in to the temptation. There
have been proposals (no more than half-joking) to eliminate

8

<<-

8

from the

language. That would not eliminate global assignments, merely force you to use
the

assign

function to achieve them.

What’s so wrong about global assignments? Surprise.
Surprise in movies and novels is good. Surprise in computer code is bad.

Except for a few functions that clearly have side effects, it is expected in

R that a function has no side effects. A function that makes a global assign-
ment violates this expectation. To users unfamiliar with the code (and even to
the writer of the code after a few weeks) there will be an object that changes
seemingly by magic.

35


background image

CIRCLE 6. DOING GLOBAL ASSIGNMENTS

Figure 6.1: The sowers of discord by Sandro Botticelli.

A particular case where global assignment is useful (and not so egregious)

is in memoization. This is when the results of computations are stored so that
if the same computation is desired later, the value can merely be looked up
rather than recomputed. The global variable is not so worrisome in this case
because it is not of direct interest to the user. There remains the problem of
name collisions—if you use the same variable name to remember values for two
different functions, disaster follows.

In R we can perform memoization by using a locally global variable. (“locally

global” is meant to be a bit humorous, but it succinctly describes what is going
on.) In this example of computing Fibonacci numbers, we are using the

8

<<-

8

operator but using it safely:

fibonacci <- local(

{

memo <- c(1, 1, rep(NA, 100))
f <- function(x)

{

if(x == 0) return(0)
if(x < 0) return(NA)
if(x > length(memo))

stop("’x’ too big for implementation")

if(!is.na(memo[x])) return(memo[x])
ans <- f(x-2) + f(x-1)
memo[x] <<- ans

36


background image

CIRCLE 6. DOING GLOBAL ASSIGNMENTS

ans

}

}

)

So what is this mumbo jumbo saying? We have a function that is just imple-
menting memoization in the naive way using the

8

<<-

8

operator. But we are

hiding the memo object in the environment local to the function. And why is

fibonacci

a function? The return value of something in curly braces is what-

ever is last. When defining a function we don’t generally name the object we
are returning, but in this case we need to name the function because it is used
recursively.

Now let’s use it:

>

fibonacci(4)

[1] 3
>

head(get(’memo’, envir=environment(fibonacci)))

[1] 1 1 2 3 NA NA

From computing the Fibonacci number for 4, the third and fourth elements of

memo

have been filled in. These values will not need to be computed again, a

mere lookup suffices.

R always passes by value. It never passes by reference.
There are two types of people: those who understand the preceding para-

graph and those who don’t.

If you don’t understand it, then R is right for you—it means that R is a

safe place (notwithstanding the load of things in this document suggesting the
contrary). Translated into humanspeak it essentially says that it is dreadfully
hard to corrupt data in R. But ingenuity knows no bounds ...

If you do understand the paragraph in question, then you’ve probably al-

ready caught on that the issue is that R is heavily influenced by functional
programming—side effects are minimized. You may also worry that this implies
hideous memory inefficiency. Well, of course, the paragraph in question is a lie.
If it were literally true, then objects (which may be very large) would always
be copied when they are arguments to functions. In fact, R attempts to only
copy objects when it is necessary, such as when the object is changed inside the
function. The paragraph is conceptually true, but not literally true.

37


background image

Circle 7

Tripping on Object
Orientation

We came upon a sinner in the seventh Circle. He said, “Below my head is the
place of those who took to simony before me—they are stuffed into the fissures
of the stone.” Indeed, with flames held to the soles of their feet.

It turns out that versions of S (of which R is a dialect) are color-coded by

the cover of books written about them. The books are: the brown book, the
blue book, the white book and the green book.

7.1

S3 methods

S3 methods correspond to the white book.

The concept in R of attributes of an object allows an exceptionally rich

set of data objects. S3 methods make the

class

attribute the driver of an

object-oriented system. It is an optional system. Only if an object has a

class

attribute do S3 methods really come into effect.

There are some functions that are

generic

. Examples include

print

,

plot

,

summary

. These functions look at the class attribute of their first argument. If

that argument does have a class attribute, then the generic function looks for a

method

of the generic function that matches the class of the argument. If such a

match exists, then the method function is used. If there is no matching method
or if the argument does not have a class, then the default method is used.

Let’s get specific. The

lm

(linear model) function returns an object of class

"lm"

. Among the methods for

print

are

print.lm

and

print.default

. The

result of a call to

lm

is printed with

print.lm

. The result of

1:10

is printed

with

print.default

.

S3 methods are simple and powerful. Objects are printed and plotted and

summarized appropriately, with no effort from the user. The user only needs to
know

print

,

plot

and

summary

.

38


background image

7.1. S3

CIRCLE 7. TRIPPING ON OBJECT ORIENTATION

There is a cost to the free lunch. That

print

is generic means that what

you see is not what you get (sometimes). In the printing of an object you may
see a number that you want—an R-squared for example—but don’t know how
to grab that number. If your mystery number is in

obj

, then there are a few

ways to look for it:

print.default(obj)
print(unclass(obj))
str(obj)

The first two print the object as if it had no class, the last prints an outline of
the structure of the object. You can also do:

names(obj)

to see what components the object has—this can give you an overview of the
object.

7.1.1

generic functions

Once upon a time a new user was appropriately inquisitive and wanted to know
how the median function worked. So, logically, the new user types the function
name to see it:

>

median

function (x, na.rm = FALSE)
UseMethod("median")
<environment: namespace:stats>

The new user then asks, “How can I find the code for

median

?”

The answer is, “You

have

found the code for

median

.”

median

is a generic

function as evidenced by the appearance of

UseMethod

. What the new user

meant to ask was, “How can I find the default method for

median

?”

The most sure-fire way of getting the method is to use

getS3method

:

getS3method(’median’, ’default’)

7.1.2

methods

The

methods

function lists the methods of a generic function. Alternatively

given a class it returns the generic functions that have methods for the class.
This statement needs a bit of qualification:

It is listing what is currently attached in the session.

It is looking at names—it will list objects in the format of

generic.class

.

It is reasonably smart, but it can be fooled into listing an object that is
not really a method.

39