Can this recursive function take less stack space (without rewriting as a loop)? - recursion

I've got some code which is running for a while and then throwing stack overflow errors. Based on the behavior and debugging, I do not think this is a case of infinite recursion, but instead of deep (but finite) and inefficient recursion.
I'm pretty convinced the following code is to blame:
fn get_descendant_leaves(&self, p: Point) -> Vec<Point> {
let children = self.get_children(p);
if children.is_empty() {
vec![p]
} else {
children
.iter()
.map(|child_p| self.get_descendant_leaves(child_p.0))
.flatten()
.collect()
}
}
Note the recursive self.get_descendant_leaves call. get_children is a non-recursive function that does a little work to recompute immediate children based on what's stored at that location (maximum of 8, usually more like 1-3). Don't worry about the self parameter - there is a lot of information hiding behind it, but here it is only needed to compute the children of a given point.
I've found that the above function hits a depth of 2-3k simultaneously from multiple threads immediately before the program crashes, which is why I'm convinced it is to blame.
I'm sure I could fix this by manually re-implementing the function to instead loop with a couple of mutable vectors - one of discovered leaves, and one of non-leaves that still need to be explored. However, I sort of suspect that this is something Rust could do for me, were I only treating it more kindly.
Is there something I could do to represent this to rust in a way that would automatically avoid the growing call stack? I'm wondering if I handled the flatten or collect differently, maybe rust's iter could would automatically handle this in a way similar to the manual solution described above. (My reasoning is that because the recursive call is in a closure that could(?) be called lazily, this seems like something the compiler might be able to unwind on its own.)
(It has to run for an hour or more to grow complex enough to crash, so experimentation is slow.)
Bonus question: Debugging this to this state took me forever, using a mix of printlns and lldb's thread backtrace to see where things were when stuff exploded. What else should have been in my toolkit for investigating this? Googling "how to diagnose rust stack overflow" turned up mostly discouraging results.
EDIT: here's a parallel structure to make debugging more fruitful. Similarly to the code above, it recursively explores a tree, building a flattened vector from "leaves" where the base condition is met.
In the example case, the "tree" exists only in theory, but can be though of as a fibbonachi-esque tree where Tree(i) has as children Tree(i-1) and Tree(i-2) for i >=1, with Tree(1) and Tree(0) as leaves.
Can this be done keeping the recursive function definition, but in such a way that Rust does not actually explode the stack in computing it?
fn main() {
let awful = awful_vec_builder(10, 0);
println!("See it's awful: {:?}", awful);
println!("Let's smash the stack:");
let more_awful = awful_vec_builder(5000, 0);
println!("Please don't reach this case: {}", more_awful.len())
}
fn awful_vec_builder(i: usize, depth: usize) -> Vec<usize> {
if i < 2 {
vec![depth]
//Note that using vec![] here still overflows the stack
} else {
let v = vec![i - 1, i - 2];
v.iter()
.map(|i| awful_vec_builder(*i, depth + 1))
.flatten()
.collect()
}
}

I think the biggest problem in your implementation is the repeated flatten and collect calls. Why do those? You basically re-construct the vector in every single call.
Instead, pass the result vector through and write directly into it:
fn main() {
let awful = awful_vec_builder(10, 0);
println!("See it's awful: {:?}", awful);
println!("Let's smash the stack:");
let more_awful = awful_vec_builder(5000, 0);
println!("Please don't reach this case: {}", more_awful.len())
}
fn awful_vec_builder(i: usize, depth: usize) -> Vec<usize> {
fn awful_vec_builder_impl(i: usize, depth: usize, result: &mut Vec<usize>) {
if i < 2 {
result.push(depth);
} else {
let v = vec![i - 1, i - 2];
for i in v {
awful_vec_builder_impl(i, depth + 1, result);
}
}
}
let mut result = Vec::new();
awful_vec_builder_impl(i, depth, &mut result);
result
}
See it's awful: [9, 9, 8, 8, 8, 8, 8, 7, 8, 8, 7, 7, 7, 8, 8, 7, 7, 7, 7, 7, 6, 8, 8, 7, 7, 7, 7, 7, 6, 7, 7, 6, 6, 6, 8, 8, 7, 7, 7, 7, 7, 6, 7, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6, 5, 8, 8, 7, 7, 7, 7, 7, 6, 7, 7, 6, 6, 6, 7, 7, 6, 6, 6, 6, 6, 5, 7, 7, 6, 6, 6, 6, 6, 5, 6, 6, 5, 5, 5]
Let's smash the stack:
... hangs for a really long time ...
For your first example, here is some boilerplate code that makes it actually compile and crash, as specified:
fn main() {
let tree = Tree;
let awful = tree.get_descendant_leaves(Point { i: 10, depth: 0 });
println!("See it's awful: {:?}", awful);
println!("Let's smash the stack:");
let more_awful = tree.get_descendant_leaves(Point { i: 5000, depth: 0 });
println!("Please don't reach this case: {}", more_awful.len())
}
struct PointWrapper(Point);
#[derive(Debug, Copy, Clone)]
struct Point {
i: usize,
depth: usize,
}
struct Tree;
impl Tree {
fn get_children(&self, p: Point) -> Vec<PointWrapper> {
if p.i < 2 {
vec![]
} else {
vec![
PointWrapper(Point {
i: p.i - 1,
depth: p.depth + 1,
}),
PointWrapper(Point {
i: p.i - 2,
depth: p.depth + 1,
}),
]
}
}
fn get_descendant_leaves(&self, p: Point) -> Vec<Point> {
let children = self.get_children(p);
if children.is_empty() {
vec![p]
} else {
children
.iter()
.map(|child_p| self.get_descendant_leaves(child_p.0))
.flatten()
.collect()
}
}
}
Similar to my other example, you could fix it like this:
fn get_descendant_leaves(&self, p: Point) -> Vec<Point> {
fn get_descendant_leaves_impl(this: &Tree, p: Point, result: &mut Vec<Point>) {
let children = this.get_children(p);
if children.is_empty() {
result.push(p);
} else {
for child_p in children {
get_descendant_leaves_impl(this, child_p.0, result);
}
}
}
let mut result = Vec::new();
get_descendant_leaves_impl(self, p, &mut result);
result
}
That said, if there is a recursive solution, there is always a better iterative solution.
Like this one:
fn get_descendant_leaves(&self, p: Point) -> Vec<Point> {
let mut result = Vec::new();
let mut queue = Vec::new();
queue.push(p);
while let Some(p) = queue.pop() {
let children = self.get_children(p);
if children.is_empty() {
result.push(p);
} else {
queue.extend(children.into_iter().map(|child| child.0).rev())
}
}
result
}

Related

How to replace portion of a vector using Rust?

What is the best way to replace a specific portion of a vector with a new vector?
As of now, I am using hardcoded code to replace the vector. What is the most effective way to achieve this?
fn main() {
let mut v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
let u = vec![0,0,0,0];
v[2] = u[0];
v[3] = u[1];
v[4] = u[2];
v[5] = u[3];
println!("v = {:?}", v);
}
Permalink to the playground
Is there any function to replace the vector with given indices?
For Copy types:
v[2..][..u.len()].copy_from_slice(&u);
Playground.
For non-Copy types:
v.splice(2..2 + u.len(), u);
Playground.
Another way:
let offset : usize = 2;
u.iter().enumerate().for_each(|(index, &val)| {
v[index + offset] = val;
});
Playground

Why does my Hailstone Sequence function using recursion only output two values?

I have the following code:
fn hailSeq(number: i32) -> Vec<i32> {
let mut vec = Vec::new();
vec.push(number);
if number == 1 {
vec.push(1);
return vec;
}
if number % 2 == 0 {
let num = number / 2;
vec.push(num);
hailSeq(num);
} else {
let num = 3 * number + 1;
vec.push(num);
hailSeq(num);
}
return vec;
}
It calculates the Hailstone sequence and stops at 1. The output should look like this for hailSeq(11):
[11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
However, my output looks like this:
[11, 34]
I am not really sure why this is occurring. Perhaps there is a limit on recursion in Rust that I don't know about, but I'm sure there's probably just an error in my code.
Your problem is not Rust-specific, but a more general problem.
On every call of hailSeq you create a new Vec every time, so that only the first vec (from the first call) would be used and returned, hence the [11, 34] (11 from the third line, 34 from the tenth line).
To fix this you have two options, I will provide one here.
The first one would be to extend the current vec with the returned vec, e.g. myvec.extend_from_slice(&returned_vec).
The second solution involves creating a vec on startup and passing the same instance to every call of the function.
fn hail_seq(number: i32) -> Vec<i32> {
fn inner(number: i32, vec: &mut Vec<i32>) {
vec.push(number);
if number == 1 {
return;
}
if number % 2 == 0 {
let num = number / 2;
inner(num, vec);
} else {
let num = 3 * number + 1;
inner(num, vec);
}
}
let mut v = vec![];
inner(number, &mut v);
v
}
fn main() {
println!("{:?}", hail_seq(11));
}
(playground)
As a side-note: If you know that a number can't be negative, use a u32 instead because you will find errors at compile time instead of runtime.

How best to find an element in nested lists?

Kotlin provides some usingful extension functions allow stream-like programming.
For example, if I look for an element in a list I can use find:
return list.find { n -> n>4 && n<6 }
But when I have a have nested lists this seems not practical for me. I have tu use forEach then -- luckyly I can return from an inner Lambda with Kotlin:
private fun findUsingForEach(data: List<List<Int>>, pred : (Int) -> Boolean) : Optional<Int> {
data.forEach { list ->
list.forEach { n ->
if( pred(n) ) return Optional.of(n)
}
}
return Optional.empty()
}
It seems fo me that forEach is not the right tool for that. Is there a more functional way to du this? filter comes to mind, but the nesting causes problems.
That follwing is the test I use for the function abouve:
#Test
open fun findTest() {
val data = listOf( listOf(1,2,3), listOf(3,4,5,6), listOf(), listOf(6,7,8) )
val e = findUsingForEach( data, { n -> n>4 && n < 6 } )
assertEquals(5, e.get())
}
You could flatten the list:
fun <T> Iterable<Iterable<T>>.flatten(): List<T> (source)
Returns a single list of all elements from all collections in the given collection.
val data = listOf(listOf(1, 2, 3), listOf(3, 4, 5, 6), listOf(), listOf(6, 7, 8))
data.flatten().find { n -> n > 4 && n < 6 }
This will return a single list with the elements of the sublists in order. Then you can use find as usual.
In your example,
{{1, 2, 3}, {3, 4, 5, 6}, {}, {6, 7, 8}}
becomes
{1, 2, 3, 3, 4, 5, 6, 6, 7, 8}
and the result of find on this list is 5.
However, this will create a new list. Take a look at the source of flatten:
/**
* Returns a single list of all elements from all collections in the given collection.
*/
public fun <T> Iterable<Iterable<T>>.flatten(): List<T> {
val result = ArrayList<T>()
for (element in this) {
result.addAll(element)
}
return result
}
If you want to save memory, create a Sequence from your list first:
data.asSequence()
and then perform your operations on this sequence:
data.asSequence().flatten().find { n -> n > 4 && n < 6 }
Side note: your predicate, n > 4 && n < 6, is simply equivalent to n == 5.
If you just want to reduce codes and you don't care much about efficiency, try this.
list.flatten().find { your pred here }
Or
list.flatMap { it }.find { your pred }
Or create a useful utility which doesn't create new lists (faster/lower memory taken):
inline fun <T> Iterable<Iterable<T>>.forEachEach(f: (T) -> Unit) =
forEach { it.forEach(f) }

How to achieve a pure function with dynamic programming in Kotlin?

I've been trying to transform some of my code to pure functions to learn how to use Kotlin in a functional way, with this simple snippet of code I can't think of any way to make my calculateFibonacci function a pure function.
I'm aware of a potentially recursive solution but what about a potential stack overflow, does Kotlin implement Tail Call Optimization?
Example:
val fibonacciValues = hashMapOf<Int, BigInteger>(0 to BigInteger.ONE, 1 to BigInteger.ONE);
// * TODO investigate how to do dynamic programming with a pure function ** //
private fun calculateFibonacci(n: Int): BigInteger? {
if (fibonacciValues.contains(n)) {
return fibonacciValues.get(n)
} else {
val f = calculateFibonacci(n - 2)!!.add(calculateFibonacci(n - 1))
fibonacciValues.put(n, f)
return f
}
}
For the whole snippet I uploaded this gist:
https://gist.github.com/moxi/e30f8e693bf044e8b6b80f8c05d4ac12
The whole thing is about breaking out of the imperative approach and thinking in terms of sequence manipulation.
In the case of the Fibonacci Sequence, it might be tricky because it's very tempting to think of it as a sequence of Ints but it gets much easier if you think of it as a sequence of pairs (from which you eventually derive a sequence of Ints)
So, you could create an infinite sequence of pairs where the next pair is defined as the second element of the previous pair and a sum of elements in a previous pair:
generateSequence(1 to 1) { it.second to it.first + it.second }
.map { it.first }
And yes, you can utilize the Tail Call Optimization by marking your method with the tailrec keyword - no worries about the stack overflow. You just apply it before the fun keyword:
fun fibonacciAt(n: Int) = {
tailrec fun fibonacciAcc(n: Int, a: Long, b: Long): Long {
return when (n == 0) {
true -> b
false -> fibonacciAcc(n - 1, a + b, a)
}
}
fibonacciAcc(n, 1, 0)
}
Here is more info about the Tail Recursion in Kotlin.
Homegrown:
fun fib(i: Int): Int {
tailrec fun go(k: Int, p: Int, c: Int): Int {
return if (k == 0) p
else go(k - 1, c, p + c)
}
return go(i, 0, 1)
}
generateSequence actually shows a Fibonacci implementation as example.
fun fibonacci(): Sequence<Int> {
// fibonacci terms
// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, ...
return generateSequence(Pair(0, 1), { Pair(it.second, it.first + it.second) }).map { it.first }
}
println(fibonacci().take(10).toList()) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
does Kotlin implements Tail Call Optimization
Yes, there is tailrec keyword for that.

How do I get a slice of a Vec<T> in Rust?

I can not find within the documentation of Vec<T> how to retrieve a slice from a specified range.
Is there something like this in the standard library:
let a = vec![1, 2, 3, 4];
let suba = a.subvector(0, 2); // Contains [1, 2];
The documentation for Vec covers this in the section titled "slicing".
You can create a slice of a Vec or array by indexing it with a Range (or RangeInclusive, RangeFrom, RangeTo, RangeToInclusive, or RangeFull), for example:
fn main() {
let a = vec![1, 2, 3, 4, 5];
// With a start and an end
println!("{:?}", &a[1..4]);
// With a start and an end, inclusive
println!("{:?}", &a[1..=3]);
// With just a start
println!("{:?}", &a[2..]);
// With just an end
println!("{:?}", &a[..3]);
// With just an end, inclusive
println!("{:?}", &a[..=2]);
// All elements
println!("{:?}", &a[..]);
}
If you wish to convert the entire Vec to a slice, you can use deref coercion:
fn main() {
let a = vec![1, 2, 3, 4, 5];
let b: &[i32] = &a;
println!("{:?}", b);
}
This coercion is automatically applied when calling a function:
fn print_it(b: &[i32]) {
println!("{:?}", b);
}
fn main() {
let a = vec![1, 2, 3, 4, 5];
print_it(&a);
}
You can also call Vec::as_slice, but it's a bit less common:
fn main() {
let a = vec![1, 2, 3, 4, 5];
let b = a.as_slice();
println!("{:?}", b);
}
See also:
Why is it discouraged to accept a reference to a String (&String), Vec (&Vec), or Box (&Box) as a function argument?

Resources