how to check if list contains lists recursively - rcpp

I have a function in R that recursively builds a list of lists of varying depth. The output node may be be
node<-list(right=(0))
or
node<-list(right=list(right=0))
In Rcpp I would like to build a recursively deconstruct the list and return the integer member, 0 in this case.
My problem is checking if node has a named member right
library(Rcpp)
cppFunction(
'
int predict(List node){
if(node["right"]){
return predict(node["right"]);
}
else{
return node;
}
}
}
'
)
I've looked at Dynamic Wrapping to check type and use switch/case, but it doesn't seem to like named lists.

Trying to check for existence of a named value using a subset, e.g. if(node["right"]), will trigger the following error:
Error in predict_bad(node) : Not compatible with requested type: [type=list; target=logical].
To search a List or *Vector for a named element, use .containsElementNamed("name") member function.
For example, we have:
#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;
}
Notice, here we are returning an Rcpp::List, e.g.
node1 = list(right = list(right = 0))
predict_list(node1)
# [[1]]
# [1] 0
To obtain only an integer, we must first subset the list and cast to the appropriate type. The second component, if we are tricky enough, we can allow Rcpp automagic to handle the conversion. (Thanks to Qiang for revealing the prior answer does not have to be positionally limited.)
#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;
}
Output:
node1 = list(right = list(right = 0))
node2 = list(right = 0)
predict_node_val(node1)
# [1] 0
predict_node_val(node2)
# [1] 0
There are a few assumptions made above... The first is we will always have a list architecture based on typing. The second is the value we want to retrieve is always listed as "right". The third

You can just get the names and check if there is an element right.
The following code should work:
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;
}
}
'
)
The results
> node<-list(right=(0))
> predict(node)
[1] 0
> node<-list(right=list(right=0))
> predict(node)
[1] 0

Related

Creating a Templated Function to Fill a Vector with another depending on Size

Is there a base function in Rcpp that:
Fills entirely by a single value if size of a vector is 1.
Fills the other vector completely if same length.
Fills with an NA value if neither Vector are the same length nor a vector is of size 1.
I've written the above criteria as a function below using a NumericVector as an example. If there isn't a base function in Rcpp that performs said operations there should be a way to template the function so that given any type of vector (e.g. numeric, character and so on) the above logic would be able to be executed.
// [[Rcpp::export]]
NumericVector cppvectorize(NumericVector x,NumericVector y) {
NumericVector y_out(y.size());
if(x.size() == 1) {
for(int i = 0; i < y_out.size(); i++) {
y_out[i] = x[0];
}
} else if(x.size() == y_out.size()) {
for(int i = 0; i < y_out.size(); i++) {
y_out[i] = x[i];
}
} else {
for(int i = 0; i < y_out.size(); i++) {
y_out[i] = NA_REAL;
}
}
return y_out;
}
Unfortunately, the closest you will come to such a function is one of the rep variants that Rcpp supports. However, none of the variants match the desired output. Therefore, the only option is to really implement a templated version of your desired function.
To create the templated function, we will first create a routing function that handles the dispatch of SEXP objects. The rationale behind the routing function is SEXP objects are able to be retrieved from and surfaced into R using Rcpp Attributes whereas a templated version is not. As a result, we need to specify the SEXTYPE (used as RTYPE) dispatches that are possible. The TYPEOF() macro retrieves the coded number. Using a switch statement, we can dispatch this number into the appropriate cases.
After dispatching, we arrive at the templated function. The templated function makes use of the base Vector class of Rcpp to simplify the data flow. From here, the notable novelty will be the use of ::traits::get_na<RTYPE>() to dynamically retrieve the appropriate NA value and fill it.
With the plan in place, let's look at the code:
#include <Rcpp.h>
using namespace Rcpp;
// ---- Templated Function
template <int RTYPE>
Vector<RTYPE> vec_helper(const Vector<RTYPE>& x, const Vector<RTYPE>& y) {
Vector<RTYPE> y_out(y.size());
if(x.size() == 1){
y_out.fill(x[0]);
} else if (x.size() == y.size()) {
y_out = x;
} else {
y_out.fill(::traits::get_na<RTYPE>());
}
return y_out;
}
// ---- Dispatch function
// [[Rcpp::export]]
SEXP cppvectorize(SEXP x, SEXP y) {
switch (TYPEOF(x)) {
case INTSXP: return vec_helper<INTSXP>(x, y);
case REALSXP: return vec_helper<REALSXP>(x, y);
case STRSXP: return vec_helper<STRSXP>(x, y);
default: Rcpp::stop("SEXP Type Not Supported.");
}
// Need to return a value even though this will never be triggered
// to quiet the compiler.
return R_NilValue;
}
Sample Tests
Here we conduct a few sample tests on each of the supported data
# Case 1: x == 1
x = 1:5
y = 2
cppvectorize(x, y)
## [1] NA
# Case 2: x == y
x = letters[1:5]
y = letters[6:10]
cppvectorize(x, y)
## [1] "a" "b" "c" "d" "e"
# Case 3: x != y && x > 1
x = 1.5
y = 2.5:6.5
cppvectorize(x, y)
## [1] 1.5 1.5 1.5 1.5 1.5

Find position of item in list using Binary Search

The question is:
Given a list of String, find a specific string in the list and return
its index in the ordered list of String sorted by mergesort. There are
two cases:
The string is in the list, return the index it should be in, in the ordered list.
The String is NOT in the list, return the index it is supposed to be in, in the ordered list.
Here is my my code, I assume that the given list is already ordered.
For 2nd case, how do I use mergesort to find the supposed index? I would appreciate some clues.
I was thinking to get a copy of the original list first, sort it, and get the index of the string in the copy list. Here I got stuck... do I use mergesort again to get the index of non-existing string in the copy list?
public static int BSearch(List<String> s, String a) {
int size = s.size();
int half = size / 2;
int index = 0;
// base case?
if (half == 0) {
if (s.get(half) == a) {
return index;
} else {
return index + 1;
}
}
// with String a
if (s.contains(a)) {
// on the right
if (s.indexOf(s) > half) {
List<String> rightHalf = s.subList(half + 1, size);
index += half;
return BSearch(rightHalf, a);
} else {
// one the left
List<String> leftHalf = s.subList(0, half - 1);
index += half;
return BSearch(leftHalf, a);
}
}
return index;
}
When I run this code, the index is not updated. I wonder what is wrong here. I only get 0 or 1 when I test the code even with the string in the list.
Your code only returns 0 or 1 because you don't keep track of your index for each recursive call, instead of resetting to 0 each time. Also, to find where the non-existent element should be, consider the list {0,2,3,5,6}. If we were to run a binary search to look for 4 here, it should stop at the index where element 5 is. Hope that's enough to get you started!

How to make the return false if the arraylist already have the string present in class?

I'm new to coding.
How do I return a false if there is a string being added that's already in the arraylist?
For example, if you have a list of dog names in the class and you add new dog names in the list, but don't add it when the same dog name was already in the list?
The Solution:
You could use a for statement to iterate through your array list:
public static bool checkArray(string dogName)
{
for int i=0; i<arrayName.Length; i++) // basic for loop to go through whole array
{
if (arrayName[i] == dogName) //checks if array value at index i is the dog's name
{
return true; //if it is, return true
}
}
return false; //gone through whole array, not found so return false
}
This means you can call your method via
string Name = "myDogsName";
bool isAlreadyPresent = checkArray(Name);
Note
This is written in C#, and so other coding languages will slightly
differ in their syntax.
isAlreadyPresent will then contain a bool value if the dog is
present or not
I have written this (for learning purposes) in (possibly) an
inefficient way, but should allow you to understand what is happening
at each stage.
the i++
The i++ may confuse new programmers, but effectively it is the same as writing
i = i + 1;
This also works for i--;
i = i - 1;
Or even i*=2;
i = i * 2;

Comparing ArrayLists containing objects

I have two ArrayLists: A and B Both ArrayLists have a bunch of objects
stored inside. A has more than B. I am trying to store the objects
that are in A but not in B in a new ArrayList called C. Each object
has a few data members but I compare only two of them - "Name" and
"OtherName".
boolean found;
for(int i = 0; i < a.size(); i++)
{
found = false;
for(int j = 0; j < b.size(); j++)
{ if (A.get(i).getName().equals(B.get(j).getName())
&&
A.get(i).getOtherName().equals(B.get(j).getOtherName()))
{
found = true;
}
}
if (found = true)
{
C.add(A.get(i));
}
}
The issue is that ArrayList 'c' is being stored with ALL the objects
from a, instead of just with the ones that are in 'a' but not in 'b'.
What am I doing wrrong?
I can see a simple mistake:
This line:
if (found = true)
{
C.add(A.get(i));
}
Needs to be:
if (found != true)
{
C.add(A.get(i));
}
You need to compare value of found to the value of true by using the not equals ("!=") sign. By using found = true. You are setting found to true every time the if statement is executed, instead of comparing the value.
That line can also be written as (They mean the same thing):
if (!found)
{
C.add(A.get(i));
}

Stack Overflow error while finding common elements between two lists

I have this code:
def input1 = ['a','b','e','r','t']
input2 = ['v','n','m','y']
ans = []
def common(def element,def i) {
if (element == input2[i]) {
ans << element
return
} else {
common(element,++i)
}
}
for (i=0;i<input1.size();i++) {
common(input1[i],0)
}
which is generating Stack Overflow error. Why is this happening?
Edit:
I'm trying to create my own way of finding common element between two lists.
You never check if i is greater than the length of input2, and in Groovy, getting beyond the length of a List returns null
So on the first element, it will keep looping round
if (element == input2[i]) {
for ever-increasing values of i, calling the common function every time, as it never matches a
Guessing at what you are trying to do, this can all be re-written as:
def input1 = ['a','b','e','r','t']
def input2 = ['v','n','m','y']
def ans = input1.intersect( input2 )
But it's hard to be sure what you want, and you dont explicitly say.
Edit
One method of deep recursion that avoids Stack Overflows is to use Groovy's trampoline method.
def common
common = { Object element, Collection list ->
if( list.size() == 0 ) { // element not found. Return null
null
}
else if( list.head() == element ) { // element found. Return it
element
}
else {
common.trampoline( element, list.tail() ) // Trampoline down the list and check again
}
}
common = common.trampoline()
def elements = ['a','b','e','v','r','t'].collect { // For each element in this list
common( it, ['v','n','m','y'] ) // Find if it's in our other list
}.findAll() // And remove the nulls
assert elements == [ 'v' ]
But I'd still use intersect in this case, the above is just to show one of Groovy's ways you can avoid too-deep recursion...
The problem is that your code doesn't stop when reaches the end of array input2. If element is not in input2 then it will keep making recursive calls common(element,++i) forever which results in stack overflow error.

Resources