Last active
August 29, 2015 14:02
-
-
Save houshuang/6ff2cb250a91d94ae70f to your computer and use it in GitHub Desktop.
Making lambdas much more dense through some ugly string manipulation + eval
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn <- function(x) { | |
if(class(x) == "function") return(x) else { | |
print(class(x)) | |
# escape percentage marks within strings | |
x = gsub("'(.+)?%([1-9]?)(.+)?'","'\\1^^^\\2\\3'", x) | |
# if a single word, call one argument, otherwise analyze arguments | |
if(grepl("^[a-zA-Z.]*$", x)) { x = paste0(x,"(x1)")} else { | |
x = gsub("%([^1-9])", "%1\\1", x) | |
x = gsub("'", "\"", x) | |
if(!grepl("%1", x)) { x = paste0("%1", x)} | |
vars = str_extract_all(string=x, pattern="%[1-9]") | |
arity = max( | |
unlist(lapply(vars, function(x) { | |
as.numeric(str_extract(string=x, pattern="\\d")) | |
}))) | |
args = Reduce(init=c(), x=(1:arity), f=function(y,x) {c(y, paste0("x", x))}) | |
for(e in 0:arity) { | |
x = gsub(paste0("%", as.character(e)), paste0("x", as.character(e)), x) | |
} | |
} | |
x = gsub("\\^\\^\\^([1-9]?)", "%\\1", x) | |
fn = paste0("function(", toString(args), ") {", x , "}") | |
eval(parse(text=fn)) | |
}} | |
map = fn("unlist(Map(fn(%1), %2))") | |
reduce = function(f, acc=c(), x,...) Reduce(fn(f), init=acc, x=x) | |
filter = function(f, x) unlist(Filter(fn(f),x)) | |
P = fn("as.character") | |
p0 = paste0 |
OK, of course you can just pass a function to map directly, without wrapping it. I now check in fn if it has been passed a function, and if that is the case, it's returned directly. Thus I can call map(class, df) and map("class", df), and they both work. At least this project is helping me learn more about the built-in functionality in R as well :)
One interesting weakness is that the eval happens in the context of the fn function, not in the original context, so for example this
get_filenames(p) %as% map("p0(p, '/', %)",
filter("grepl('^[^~]', %)", list.files(path=p)))
does not work properly, because p
in the map function is not defined when the built-up string gets evaluated by fn
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I added a special case if the function definition is a single word only consisting of lower-case or upper-case letters. This allows me to do
map("class", dataframe)
, which is turned intofunction(x) class(x)
. I can still domap("*2", c(1,2,3))
, which turns intofunction(x) x*2
. If there's any ambiguity, you can always specify the % arguments, which will override this test.I also check if % is within a string, and ignore it if it is. And I added some abbreviation for common functions I use. This makes me able to quickly generate a list of percentages like below.