Items
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
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 about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.