Как проверить, если список содержит списки рекурсивно

У меня есть функция в R, которая рекурсивно создает список списков различной глубины. Выход node может быть

node<-list(right=(0))

или же

node<-list(right=list(right=0))

В Rcpp я хотел бы построить рекурсивно деконструировать список и вернуть целочисленный член, 0 в этом случае.

Моя проблема заключается в проверке, если node имеет именованного члена right

library(Rcpp)

cppFunction(
'
int predict(List node){
   if(node["right"]){
     return predict(node["right"]);
   }
   else{
     return node;
   }
 }
}
'
)

Я посмотрел на Dynamic Wrapping, чтобы проверить тип и использовать switch / case, но он не похож на именованные списки.

2 ответа

Решение

Попытка проверить наличие именованного значения, используя подмножество, например if(node["right"]), вызовет следующую ошибку:

Error in predict_bad(node) : Not compatible with requested type: [type=list; target=logical].

Чтобы найти List или же *Vector для именованного элемента используйте .containsElementNamed("name") функция-член.

Например, у нас есть:

#include<Rcpp.h>

// [[Rcpp::export]]
Rcpp::List predict_list(Rcpp::List node){

    // Check if name is present
    if(node.containsElementNamed("right")) {
        return predict_list(node["right"]);
    }

    return node;
}

Обратите внимание, здесь мы возвращаем Rcpp::List например,

node1 = list(right = list(right = 0))  
predict_list(node1)
# [[1]]
# [1] 0

Чтобы получить только целое число, мы должны сначала поместить список в список и привести к соответствующему типу. Второй компонент, если мы достаточно хитры, мы можем позволить Rcpp automagic обрабатывать преобразование. (Спасибо Цянгу за то, что он раскрыл предыдущий ответ, не должен быть позиционно ограничен.)

#include<Rcpp.h>

// [[Rcpp::export]]
int predict_node_val(Rcpp::List node) {

    // Check if name is present
    if(node.containsElementNamed("right")) {

        // Check if element isn't a list.
        switch(TYPEOF(node["right"])) {
        case REALSXP:
        case INTSXP:
            return node["right"];
        default: // Keep going down the tree
            return predict_node_val(node["right"]);
        }

    }

    // Quiet compiler by providing a final output case
    return -1;
}

Выход:

node1 = list(right = list(right = 0))  
node2 = list(right = 0)  
predict_node_val(node1)
# [1] 0
predict_node_val(node2)
# [1] 0

Есть несколько предположений, сделанных выше... Во-первых, у нас всегда будет архитектура списка, основанная на типизации. Второе значение, которое мы хотим получить, всегда указывается как "right", Третий

Вы можете просто получить имена и проверить, есть ли элемент right,

Следующий код должен работать:

library(Rcpp)

cppFunction(
  '
  int predict(List node) {
    std::vector<std::string> list_names = node.names();
    if (std::find(list_names.begin(), list_names.end(), "right") != list_names.end()) {
      if (TYPEOF(node["right"]) == REALSXP) {
        return node["right"];
      } else {
        return predict(node["right"]);
      }
    } else {
      return -1;
    }
  }
  '
)

Результаты, достижения

> node<-list(right=(0))
> predict(node)
[1] 0
> node<-list(right=list(right=0))
> predict(node)
[1] 0
Другие вопросы по тегам