2

I am trying to extract an element from a list and do something with it's value using Rcpp. But I cannot make the assignment.

Here is basically what I want to achieve given in R code:

mylist = list(steps = list(`1` = list(a = 7, b = "abc"), 
                           `2` = list(a = 3), 
                           `3` = list(a = 5, b = NULL)))
# This is the desired behavior that I can program in R
step_name = "1"
b = ifelse(is.null(mylist$steps[[step_name]][["b"]]), 
           "", mylist$steps[[step_name]][["b"]])
# Do something with the value of b

The following code cannot make the assignment to b. The value of a is extracted as it should. I don't know what I'm missing here.

library(Rcpp)
cppFunction('int foo(Rcpp::List x, std::string step_name) {
  Rcpp::List steps = x("steps");
  Rcpp::List step = steps(step_name);
  int a = step("a");
  //// Not declaring b causes "not declared in this scope" error 
  //// so I initialize it with a random value.
  std::string b = "random";
  if (step.containsElementNamed("b")){
    Rcout << "b is in List!" << "\\n";
    if (!Rf_isNull(step("b"))) {
      Rcout << "b is not NULL!" << "\\n";
      if (TYPEOF(step("b")) == STRSXP)
        Rcout << "b is character!" << "\\n";
      std::string b = step("b");
    } else {
      Rcout << "b is NULL!" << "\\n";
      std::string b = "efg";
    }          
  } else {
    Rcout << "b is not in List!" << "\\n";
    std::string b = "xyz";
  }
  Rcout << "The Value of b is " << b << ".\\n";
  if (b == "abc") {
    //// Do something with the value of b
  }       
  return a;
}')

foo(mylist, "1")
## b is in List!
## b is not NULL!
## b is character!
## The Value of b is random.
## [1] 7
foo(mylist, "2")
## b is not in List!
## The Value of b is random.
## [1] 3
foo(mylist, "3")
## b is in List!
## b is NULL!
## The Value of b is random.
## [1] 5
HBat
  • 4,873
  • 4
  • 39
  • 56
  • 2
    I answered nested list questions before here, maybe try some searches. I would also recommend to try first with just a list, then a list in a list, then a test for NULL. I.e. decompose the problem if you are currently stuck. – Dirk Eddelbuettel Feb 08 '20 at 15:39
  • I successfully used decomposing the list strategy for `a` but not for `b`. – HBat Feb 08 '20 at 15:53
  • 1
    I also believe that answering/commenting questions by plain "search the internet.." is not helpful because majority of the SO questions can be answered by searching internet for sufficient amount of time. I'll appreciate if you can point out a duplicate question or a link that will answer this question otherwise please refrain from commenting. Because the suggestion you offered was already tried (with my limited capacity) and shown in the question. As you assumed that I have not made sufficient search, I may also assume that you have not read the question but commenting just based on the title – HBat Feb 08 '20 at 16:20
  • Look, I was trying to help you while at the same working on two other things here. I appear to have answered 3500 questions here over the years, but I am not obligated to either repeat myself for you. _Nested lists work just fine in Rcpp_ was meant as a encouragement, you likely have a plain vanilla bug sitting here but given your tone I will refrain from spending _my time_ on finding it for you. – Dirk Eddelbuettel Feb 08 '20 at 16:26
  • 1
    I don't want to be disrespectful and I appreciate your time. Possibly the problem is an obvious one for an expert eye. I just wanted to express my disagreement with the assumption that I have not done sufficient search before submitting the question. – HBat Feb 08 '20 at 16:40
  • Why do you declare ```b``` again in the ```if then``` statements? That's probably the problem but I am not at a PC to test. – Cole Feb 08 '20 at 16:47
  • Not a specific reason. When I remove `std::string ` from all but the first declaration I got `error: ambiguous overload for 'operator=' (operand types are 'std::string {aka std::basic_string}' and 'Rcpp::Vector<19>::NameProxy`. – HBat Feb 08 '20 at 16:52

1 Answers1

3

It appears to mostly be a scoping issue and a variable type issue. I believe the std::string b declarations in the if then statements are local. Any changes there do not last.

Then, the errors in your comments are trying to assign the LHS std::string with RHS Rcpp:Vector. While I am sure there are better ways to convert and/or simplify, the solution below just declares Rcpp::CharacterVector b.

library(Rcpp)
cppFunction('int foo(Rcpp::List x, std::string step_name) {
  Rcpp::List steps = x("steps");
  Rcpp::List step = steps(step_name);
  int a = step("a");
  //// Not declaring b causes "not declared in this scope" error 
  //// so I initialize it with a random value.
  CharacterVector b; #############
  if (step.containsElementNamed("b")){
    Rcout << "b is in List!" << "\\n";
    if (!Rf_isNull(step("b"))) {
      Rcout << "b is not NULL!" << "\\n";
      if (TYPEOF(step("b")) == STRSXP)
        Rcout << "b is character!" << "\\n";
      b = step("b");
    } else {
      Rcout << "b is NULL!" << "\\n";
    }          
  } else {
    Rcout << "b is not in List!" << "\\n";
  }
  Rcout << "The size of b is " << b.size() << ".\\n"; #########
  if (b.size() > 0 && b[0] == "abc") { ################
    Rcout << "Do something with b.\\n";
    //// Do something with the value of b
  }       
  return a;
}')

mylist = list(steps = list(`1` = list(a = 7, b = "abc"), 
                           `2` = list(a = 3), 
                           `3` = list(a = 5, b = NULL)))

foo(mylist, "1")
# b is in List!
# b is not NULL!
# b is character!
# The size of b is 1.
# Do something with b.
# [1] 7
foo(mylist, "2")
# b is not in List!
# The size of b is 0.
# [1] 3
foo(mylist, "3")
# b is in List!
# b is NULL!
# The size of b is 0.
# [1] 5
Cole
  • 11,130
  • 1
  • 9
  • 24
  • 1
    This is very helpful, especially for giving me the keywords to look for 'scope' and 'type'. I searched for them and come up with this code, with the help of [this](https://stackoverflow.com/a/8422324/2275286) and [this](https://stackoverflow.com/a/8543179/2275286), which is equivalent of what I was trying to do while still preserving the type `std::string`: `std::string b = step.containsElementNamed("b") && !Rf_isNull(step("b")) ? Rcpp::as(step("b")) : "";` – HBat Feb 08 '20 at 19:21