Items

June 22, 2011
By

(This article was first published on Struggling Through Problems, and kindly contributed to R-bloggers)

Let’s define a new pattern!

I do a lot of programming for research, and part of what this involves is
turning calculations, usually expressed as verbs, into nouns. That is, I need
to keep the calculation and all it’s intermediate steps around so that I can
inspect them.

Alas, I am programming in Python most of the time and end up writing
a lot of code like this:

class Foo:

def __init__(self, tol, angle):
self.tol   = tol
self.angle = angle

...

self.a    = a
self.b    = b
self.step = step


ie manually saving all the steps. But in R we can do better.
Imagine we could write code like:

> Center.Find = item(

> # These define the parameters

> t =,

> x =,

>

> # These give the calculation

> theta = Arg(x),

> dtheta = diff(theta) / diff(t),

> center = median(dtheta[dethat>0])

> )

where all the variables defined in the item become
named members of a list, and we can inspect all of them.

We could try something like this:

> item = function() {

> foo = function() {

> environment()

> }

>

> args = process.args(as.list(substitute(list()))[1L])

> formals(foo) = args

>

> foo

> }

That is, the names defined in the item become function
parameters, which get included in the environment. process.args is
an unfortunate necessity that flips the argument list.

> process.args = function(args) {

> arg.names = names(args)

> if (is.null(arg.names)) {

> arg.names = replicate(length(args), NULL)

> }

>

> names(args) = arg.names

>

> args

> }

> A = item(

> x =,

> y =,

>

> sum = x + y

> )

No error yet…

> a = A(2, 3)

> as.list(a)

$x [1] 2$y

[1] 3

$sum [1] 5 Excellent. Notice that we get a few other features for free: > A = item( > sum = x + y, > > x =, > y = > ) > a = A(x=2, y=3) > as.list(a)$sum

[1] 5

$x [1] 2$y

[1] 3

Lazy evaluation means we can define variables in any order; also ones we
don’t use will never be evaluated. This saves me from a common dilemma: I want
to include lots of diagnostic information just in case I need it, but I don’t
want to wait for it to be calculated. Now I can add in any extra info I can
imagine using, and it won’t be calculated unless I need it.

Another nice feature is you can “reach in” to the item
and change its behavior — not terribly good style, but this is research
code
.

> a = A(x=2, y=3, sum=8)

> as.list(a)

$sum [1] 8$x

[1] 2

$y [1] 3 Notice, however, that this (which would really be the only useful purpose of “reaching in”), fails: > a = A(x=2, y=3, sum = x 1) > try(as.list(a), silent=T) > geterrmessage() [1] “Error in as.list.environment(a) : object ‘x’ not found\n” So, what if we wanted to defy common sense and make it possible to modify the behiavor of an item at instantiation time? Well remember how the result of calling an item is an environmenth? Uh oh… > a = A( > x = 2, > y = 3, > sum = with(a, x 1) > ) > as.list(a)$sum

[1] 1

$x [1] 2$y

[1] 3

Of course this means we cannot modify the behavior of an anonymous
item. I couldn’t think of a way to do that…

R-bloggers.com offers daily e-mail updates about R news and tutorials on topics such as: Data science, Big Data, R jobs, visualization (ggplot2, Boxplots, maps, animation), programming (RStudio, Sweave, LaTeX, SQL, Eclipse, git, hadoop, Web Scraping) statistics (regression, PCA, time series, trading) and more...