'Is there a way to convert Python to R?

Hey I am trying to convert my python code to R and can't seem to figure out the last part of the recursion. If anyone who has experience in both languages could help that would be great!

def robber(nums):
    if len(nums) == 0: return 0
    elif len(nums) <= 2: return max(nums)
    else:
        A = [nums[0], max(nums[0:2])]
        for i in range(2, len(nums)):
            A.append(max(A[i-1], A[i-2] + nums[i]))
    return A[-1]

Above is the Python version and below is my attempt so far on converting to R

robbing <- function(nums) {
    if (length(nums) == 0){
        result <- 0      
    }
    else if(length(nums) <= 2){
        result <- max(nums)     
    }
    else{
      a <- list(nums[0], max(nums(0:2)))
      for (i in range(2, length(nums))){
          result <- max(a[i-1], a[i-2] + nums[i])
      }
    }
    #result <- a[-1]
}


Solution 1:[1]

You have a couple of problems.

  1. You are zero-indexing your vectors. R is 1-indexed (first element of y is y[1] not y[0].
  2. Ranges (slices in python) in R are inclusive. Eg: 0:2 = c(0, 1, 2) while python is right-exclusive 0:2 = [0, 1].
  3. R uses minus elements to "remove" elements of vectors, while Python uses these to extract from reverse order. Eg: y[-1] = y[2:length(y)] in R.
  4. R's range function is not the same as Python's range function. The equivalent in R would be seq or a:b (example 3:n). Not again that it is right-inclusive while pythons is right-exclusive!
  5. You are not storing your intermediary results in a as you are doing in python. You need to do this at run-time

And last: R functions will return the last evaluation by default. So there is no need to explicitly use return. This is not a problem per-say, but something that can make code look cleaner (or less clean in some cases). So one option to fix you problem would be:

robber <- function(nums){
  n <- length(nums) # <= Only compute length **once** =>
  if(n == 0)
    0 # <= Returned because no more code is run after this =>
  else if(n <= 2)
    max(nums) # <= Returned because no more code is run after this =>
  else{
    a <- numeric(n) # <= pre-allocate our vector =>
    a[1:2] <- cummax(nums[1:2]) # <= Cummax instead of c(nums[1], max(nums[1:2])) =>
    for(i in 3:n){ # <= Note that we start at 3, because of R's 1-indexing =>
      a[i] <- max(a[i - 1], a[i - 2] + nums[i])
    }
    a[n]
  }
}

Note 3 things:

  1. I use that R vectors are 1-indexed, and my range goes from 3 as a consequence of this.
  2. I pre-allocate my a vector (here using numeric(n)). R vector expansion is slow while python lists are constant in time-complexity. So preallocation is the recommended way to go in all cases.
  3. I extract my length once and store it in a variable. n <- length(nums). It is inherently unnecessary to evaluate this multiple times, and it is recommended to store these intermediary results in a variable. This goes for any language such as R, Python and even in compild languages such as C++ (while for the latter, in many cases the compiler is smart enough to not recompute the result).

Last I use cummax where I can. I feel there is an optimized way to get your result almost immediately using vectorization, but I can't quite see it.

Solution 2:[2]

I would avoid to use a list. Because appending lists is slow. (Especially in R! - Vector is much better. But we don't need any sequence and indexing, if we use variables like I show you here). You don't need to build a list. All you need to keep in memory is the previous and the preprevious value for res.

def robber(nums, res=0, prev=0, preprev=0): # local vars predefined here
    for x in nums:
        prev, preprev = res, prev
        res = max(prev, preprev + x)
    return res

This python function does the same like your given. (Try it out!).

In R this would be:

robber <- function(nums, res=0, prev=0, preprev=0) {
  for (x in nums) {
    preprev <- prev
    prev <- res       # correct order important!
    res <- max(prev, preprev + x)
  }
  res
}

Taking the local variable definitions into the argument list saves in R 3 lines of code, therefore I did it.

Solution 3:[3]

I suggest you can change result to return() and renaming object a outside the function, also change len to length() by the end of the function.

a <- list(nums[0], max(nums(0:2)))

robbing <- function(nums) {
  if (length(nums) == 0){
    return(0)    
  }
  else if(length(nums) <= 2){
    return(max(nums))     
  }
  else{
    
    for (i in range(2, length(nums))){
      return(max(a[i-1], a[i-2] + nums[i]))
    }
  }
  return(a[length(a)])
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 TheFunSideofData