the following is a trivialized version of some functional code i tried
to use in r:
(funcs = lapply(1:5, function(i) function() i))
# a list of no-parameter functions, each with its own closure environment,
# each supposed to return the corresponding index when applied to no
arguments
sapply(funcs, function(func) func())
# supposed to return c(1,2,3,4,5)
there is absolutely nothing unusual in this code, in the context of
functional programming.
the following is my best translation to python (modulo indexing, which
is inessential), where it does what i wanted:
funcs = map(lambda i: lambda: i, range(5))
map(lambda func: func(), funcs)
# [0,1,2,3,4]
all these functions have distinct environments:
(envs = sapply(funcs, function(func) environment(func)))
assign("i", 0, environment(envs[[1]]))
sapply(funcs, function(func) func())
interestingly, identical says they are all unequal, but compare disagrees.
check = function(equal) for (i in 1:4) for (j in i:4+1)
print(equal(envs[[i]], envs[[j]]))
check(identical)
check(compare)
compare seems to cast environments to character; for identical, the docs
give an example where environments are compared, but compare fails
(i.e., succeeds) miserably (the docs do not warn not to compare
environments):
e1 = new.env(parent=emptyenv())
e2 = new.env(parent=emptyenv())
assign("foo", "bar", e1)
compare(e1, e2)
# oops?
back to the original example, how come?
vQ
----
for those curious, try the following in python:
map(lambda func: func(), [lambda: i for i in range(5)])
map(lambda func: func(), (lambda: i for i in range(5)))
so just to show how scheme would do that:
(map
(lambda (function) (function))
(map
(lambda (index) (lambda () index))
'(1 2 3 4 5)))
;; *this* is the scheme semantics
vQ
xxx wrote:> I agree, something is wrong here, especially since the R community
> likes to say that R semantics are based on Scheme....
>
> On Mon, Nov 17, 2008 at 4:28 PM, Wacek Kusnierczyk
> <Waclaw.Marcin.Kusnierczyk at idi.ntnu.no> wrote:
>
>> the following is a trivialized version of some functional code i tried
>> to use in r:
>>
>> (funcs = lapply(1:5, function(i) function() i))
>> # a list of no-parameter functions, each with its own closure
environment,
>> # each supposed to return the corresponding index when applied to no
>> arguments
>>
>> sapply(funcs, function(func) func())
>> # supposed to return c(1,2,3,4,5)
>>
>> there is absolutely nothing unusual in this code, in the context of
>> functional programming.
>>
Wacek,
I think when people say that R semantics are derived from Scheme, all
they mean is that R supports lexical closures. But R has other
features which are very un-Scheme-like, and when they interact with
lexical closures, you get behavior you don't find in other functional
languages.
R passes function arguments using a "promise" mechanism somewhat
similar to Algol 60's call-by-name, but caching the result when it is
evaluated, what is sometimes called call-by-need. If the argument
value is not needed, it keeps it as a "promise" (what Algol 60 called
a "thunk").
Thus, we have:
lapply(1:5,function(i) function( ) i )[[1]]() => 5
since the inner i is actually bound to a 'promise' which seems to
point to the variable used by lapply for counting.
Compare with the result of:
lapply(1:5,function(i) { i<-i; function() i } )[[1]]() => 1
Scheme of course does not have the "promise" mechanism so this
doesn't happen.
I'm new to R, so I don't know if this is considered a bug or a feature.
-s
On Mon, Nov 17, 2008 at 4:28 PM, Wacek Kusnierczyk
<Waclaw.Marcin.Kusnierczyk at idi.ntnu.no> wrote:> the following is a trivialized version of some functional code i tried
> to use in r:
>
> (funcs = lapply(1:5, function(i) function() i))
> # a list of no-parameter functions, each with its own closure environment,
> # each supposed to return the corresponding index when applied to no
> arguments
>
> sapply(funcs, function(func) func())
> # supposed to return c(1,2,3,4,5)
>
> there is absolutely nothing unusual in this code, in the context of
> functional programming.
> the following is my best translation to python (modulo indexing, which
> is inessential), where it does what i wanted:
>
> funcs = map(lambda i: lambda: i, range(5))
> map(lambda func: func(), funcs)
> # [0,1,2,3,4]
>
> all these functions have distinct environments:
>
> (envs = sapply(funcs, function(func) environment(func)))
> assign("i", 0, environment(envs[[1]]))
> sapply(funcs, function(func) func())
>
> interestingly, identical says they are all unequal, but compare disagrees.
>
> check = function(equal) for (i in 1:4) for (j in i:4+1)
> print(equal(envs[[i]], envs[[j]]))
> check(identical)
> check(compare)
>
> compare seems to cast environments to character; for identical, the docs
> give an example where environments are compared, but compare fails
> (i.e., succeeds) miserably (the docs do not warn not to compare
> environments):
>
> e1 = new.env(parent=emptyenv())
> e2 = new.env(parent=emptyenv())
> assign("foo", "bar", e1)
> compare(e1, e2)
> # oops?
>
>
> back to the original example, how come?
>
> vQ
>
>
> ----
> for those curious, try the following in python:
>
> map(lambda func: func(), [lambda: i for i in range(5)])
> map(lambda func: func(), (lambda: i for i in range(5)))
>
> ______________________________________________
> R-help at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide
http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
>
On Mon, 17 Nov 2008, Wacek Kusnierczyk wrote:> the following is a trivialized version of some functional code i tried > to use in r: > > (funcs = lapply(1:5, function(i) function() i)) > # a list of no-parameter functions, each with its own closure environment, > # each supposed to return the corresponding index when applied to no > arguments > > sapply(funcs, function(func) func()) > # supposed to return c(1,2,3,4,5) > > there is absolutely nothing unusual in this code, in the context of > functional programming.What is unusual is R's lazy evaluation. Each function gets a promise to evaluate i, but the promise isn't forced until i is 5. Lazy evaluation is sometimes confusing, but it's the price R pays for not having macros. If you want to get around this (rather than just to construct an example), use force(). (funcs = lapply(1:5, function(i) {force(i);function() i})) -thomas Thomas Lumley Assoc. Professor, Biostatistics tlumley at u.washington.edu University of Washington, Seattle
You can use the 'local' function to make sure you create a value of 'i' that is defined when the function is defined:> funcs = lapply(1:5, function(i)local({i; function(){ i}})) > funcs[[3]]()[1] 3> funcs[[2]]()[1] 2 On Mon, Nov 17, 2008 at 4:28 PM, Wacek Kusnierczyk <Waclaw.Marcin.Kusnierczyk at idi.ntnu.no> wrote:> the following is a trivialized version of some functional code i tried > to use in r: > > (funcs = lapply(1:5, function(i) function() i)) > # a list of no-parameter functions, each with its own closure environment, > # each supposed to return the corresponding index when applied to no > arguments > > sapply(funcs, function(func) func()) > # supposed to return c(1,2,3,4,5) > > there is absolutely nothing unusual in this code, in the context of > functional programming. > the following is my best translation to python (modulo indexing, which > is inessential), where it does what i wanted: > > funcs = map(lambda i: lambda: i, range(5)) > map(lambda func: func(), funcs) > # [0,1,2,3,4] > > all these functions have distinct environments: > > (envs = sapply(funcs, function(func) environment(func))) > assign("i", 0, environment(envs[[1]])) > sapply(funcs, function(func) func()) > > interestingly, identical says they are all unequal, but compare disagrees. > > check = function(equal) for (i in 1:4) for (j in i:4+1) > print(equal(envs[[i]], envs[[j]])) > check(identical) > check(compare) > > compare seems to cast environments to character; for identical, the docs > give an example where environments are compared, but compare fails > (i.e., succeeds) miserably (the docs do not warn not to compare > environments): > > e1 = new.env(parent=emptyenv()) > e2 = new.env(parent=emptyenv()) > assign("foo", "bar", e1) > compare(e1, e2) > # oops? > > > back to the original example, how come? > > vQ > > > ---- > for those curious, try the following in python: > > map(lambda func: func(), [lambda: i for i in range(5)]) > map(lambda func: func(), (lambda: i for i in range(5))) > > ______________________________________________ > R-help at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code. >-- Jim Holtman Cincinnati, OH +1 513 646 9390 What is the problem that you are trying to solve?