Как проверить, если список содержит списки рекурсивно
У меня есть функция в 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