Skip to content

Recursive function calls

11 messages · Bert Gunter, Rui Barradas, Hadley Wickham +2 more

#
"Recursively loop over an object" is a pretty meaningless phrase,
since it depends entirely on the structure of the object. For example,
a character vector is an object, and there is no need for any sort of
recursion to do what you want for it.

The following regex example trims trailing "spaces" (see ?regex for an
exact definition). Adapt it to whatever structure you like, probably
with apply type functions.
[1] "   ab   " "ab  \t  "  "\t\t"
[1] "   ab" "ab"    ""

But note also that (e.g. S3) methods and is.list or is.recursive may
be useful in a more general approach, something like (where deSpace(x)
is a function with the above sub() expression):

nospace <- function(x){
if(is.atomic(x))deSpace(x)
else lapply(x, function(y)Recall(y))  ##?Recall for recursion
}

Note that this is completely untested, probably fails miserably and
isn't what you want anyway, ...
;-)
Good luck!

Cheers,
Bert
On Fri, Aug 3, 2012 at 9:12 AM, Gene Leynes <gleynes at gmail.com> wrote:

  
    
#
Hello,

This seems to work.

trim2 <- function(x) {
     if(is.atomic(x))
         gsub("^[[:space:]]+|[[:space:]]+$", "", x)
     else
         sapply(x, function(y) trim2(y))
}

# Tests
trim2(tempobj)
trim2(tempvec)
trim2(templist)
trim2(tempdf)

# Extra test
templistlist <- list(templist, list(tempobj, tempdf))
trim2(templistlist)

Note, however, that the df is not returned as a df:

 > class(trim2(tempdf))
[1] "matrix"

Hope this helps,

Rui Barradas
Em 03-08-2012 17:12, Gene Leynes escreveu:
#
Note that this is a common enough case that Hadley provides for it
with the str_trim() function in his stringr package.

Best,
Michael
On Fri, Aug 3, 2012 at 12:02 PM, Bert Gunter <gunter.berton at gene.com> wrote:
#
On Fri, Aug 3, 2012 at 12:19 PM, Rui Barradas <ruipbarradas at sapo.pt> wrote:
Using sapply is a bit dangerous here. Compare:

trim2(list(c("a", "b"), c("c", "d")))
#      [,1] [,2]
# [1,] "a"  "c"
# [2,] "b"  "d"

trim2(list(c("a", "b"), c("c", "d", "e")))
# [[1]]
# [1] "a" "b"
#
# [[2]]
# [1] "c" "d" "e"

which I think is rather undesirable behaviour. sapply is suitable for
interactive use, but you should never use it inside a function because
you don't know what sort of data structure you'll get back.

I think it's also a bit unsafe to accept any type of input - you're
generally better off being explicit.  This leads to trim3:

trim3 <- function(x) {
  if (is.character(x)) {
    gsub("^[[:space:]]+|[[:space:]]+$", "", x)
  } else if (is.list(x)) {
    lapply(x, trim3)
  } else {
    warning("Invalid input: ", paste(class(x), sep = "/"))
    x
  }
}

trim2(list(c("a", "b"), c("c", "d")))
trim3(list(c("a", "b"), c("c", "d")))

But then the function isn't extensible for new types of input, which
suggests an S3 implementation:

trim4 <- function(x) UseMethod("trim4")
trim4.character <- function(x) gsub("^[[:space:]]+|[[:space:]]+$", "", x)
trim4.list <- function(x) lapply(x, trim4)
trim4.default <- function(x) {
  warning("Invalid input")
  x
}

Hadley
#
On Fri, Aug 3, 2012 at 3:36 PM, Gene Leynes <gleynes at gmail.com> wrote:
One other helpful function that I haven't seen anyone mention this far
is  ? rapply [= recursive apply]

Best,
Michael
#
[1] 2376

Over two thousand objects seems like rather a lot to me...

Hadley
#
On Fri, Aug 3, 2012 at 5:41 PM, Gene Leynes <gleynes at gmail.com> wrote:
Possibly, though I suppose it's a matter of judgement, even more
straightforward to just make your input into a list and don't special
case:

trimmer <- function(x){rapply(list(x),  function(x)
gsub("^[[:space:]]+|[[:space:]]+$", "", x), how = "replace")[[1]]}

Note the "[[1]]" to undo the list() call we added.

Best,
Michael