Recursion transformation without stack frames code repetitions - algorithm

I have the following pseudo-code:
function X(data, limit, level = 0)
{
result = [];
foreach (Y(data, level) as entity) {
if (level < limit) {
result = result + X(entity, limit, level + 1);
} else {
//trivial recursion case:
result = result + Z(entity);
}
}
return result;
}
which I need to turn into a plain (e.g. without recursive calls). So far I'm out of ideas regarding how to do that elegantly. Following this answer I see that I must construct the entire stack frames which are basically the code repetitions (i.e. I will place same code again and again with different return addresses).
Or I tried stuff like these suggestions - where there is a phrase
Find a recursive call that’s not a tail call.
Identify what work is being done between that call and its return statement.
But I do not understand how can the "work" be identified in the case when it is happening from within internal loop.
So, my problem is that all examples above are providing cases when the "work can be easily identified" because there are no control instructions from within the function. I understand the concept behind recursion on a compilation level, but what I want to avoid is code repetition. So,
My question: how to approach transformation of the pseudo-code above which does not mean code repetitions for simulating stack frames?

It looks like an algorithm to descend a nested data structure (lists of lists) and flatten it out into a single list. It would have been good to have a simple description like that in the question.
To do that, you need to keep track of multiple indices / iterators / cursors, one at each level that you've descended through. A recursive implementation does that by using the call stack. A non-recursive implementation will need a manually-implemented stack data structure where you can push/pop stuff.
Since you don't have to save context (registers) and a return address on the call stack, just the actual iterator (e.g. array index), this can be a lot more space efficient.
When you're looping over the result of Y and need to call X or Z, push the current state onto the stack. Branch back to the beginning of the foreach, and call Y on the new entity. When you get to the end of a loop, pop the old state if there is any, and pick up in the middle of that loop.

Related

Can someone explain me this code intuitively?

I understand recursion and what the advantages it brings to writing code efficiently. While I can code recursive functions, I cannot seem to wrap my head around how they work. I would like someone to explain me recursion instinctively.
For example, this code:
int fact(int n)
{ if n<0:
return -1
elif n==0:
return 1
else
return n*fact(n-1)
}
These are some of my questions:
Let's say n=5. On entering the function,the control goes to the last return statement since none of the previous conditions are satisfied.
Now, roughly, the computer 'writes' something like this: 5*(fact(4))
Again, the fact() function is called and the same process gets repeated except now we have n=4.
So, how exactly does the compiler multiply 5*4 and so on until 2 since its not exactly 5*4 but 5*fact(4). How does it 'remember' that it has to multiply two integers and where does it store the temporary value since we haven't provided any explicit data structure?
Again let's say n=5. The same process goes on and eventually n gets decremented to 0. My question is why/how doesn't the function simply return 1 as stated in the return statement. Similar to my previous question, how does the compiler 'remember' that it also has 180 stored for displaying?
I'd be really thankful if someone explains this to me completely so that can understand recursion better and intuitively.
Yeah, for beginners recursion can be quite confusing. But, you are already on the right track with your explanation under "1.".
The function will be called recursively until a break condition is satisfied. In this case, the break condition is satisfied when n equals 0. At this point, no recursive calls will be made anymore. The result of each recursive call is returned to the caller. The callers always "wait" until they get a result. That's how the algorithm "knows" the receiver of the results. The flow of this procedure is handled by the so called stack.
Hence, in your informal notation (in this example n equals 3):
3*(fact(2)) = 3*(2*fact(1)) = 3*(2*(1*fact(0))).
Now, n equals 0. The inner fact(0) therefore returns 1:
3*(2*(1*(1)))) = 3*(2*(1)) = 3*(2) = 6
You can see a bit like this
The function fact(int n) is like a class and every time you call fact(int n) you create an instance of that class. By creating them (calling them) from the same function, you are creating a chain of instances. Once you reach break condition, those functions start returning one by one and the value they returned to calculate a new value in the return statement return n*fact(n-1) e.g. return 3*fact(2);

recursion in sorted double linked list insertion

I'm new to the data structures and recursion concept. I'm struggling to understand why and who he was able to use the recursion in this concept. I found this code in the forums for this and I couldn't really understand the concept of this. For simple case of 2 1 3 4, if any one can explain the iteration steps, it will be greatly appreciated on my behalf.
Here is the link for hacker rank:
https://www.hackerrank.com/challenges/insert-a-node-into-a-sorted-doubly-linked-list
Node SortedInsert(Node head,int data) {
Node n = new Node();
n.data = data;
if (head == null) {
return n;
}
else if (data <= head.data) {
n.next = head;
head.prev = n;
return n;
}
else {
Node rest = SortedInsert(head.next, data);
head.next = rest;
rest.prev = head;
return head;
}
}
Recursion:
Recursion means a function calls itself. It is used as a simple way to save state information for algorithms that require saving of multiple states, usually a large number of states, and retrieving them in reverse order. (There are alternative techniques that are more professional and less prone to memory issues, such as using a Stack object to save program state).
This example is poor but typical of intro to recursion. Yes, you can iterate through a linked list using recursion but there is absolutely no reason to. A loop would be more appropriate. This is purely for demonstrating how recursion works. So, to answer your question "Why?" it is simply so you can learn the concept and use it later in other algorithms that it actually makes sense.
Recursion is useful when instead of a linked list you have a tree, where each node points to multiple other nodes. In that case, you need to save your state (which node you are on, and which subnode you called last) so that you can traversing one of the linked nodes, then return and go to the next node.
You also asked "how". When a function calls itself, all of its variables are saved (on the program stack) and new ones are created for the next iteration of itself. Then, when that call returns, it goes back to where it was called from and the previous set of variables are loaded. This is very different from a "jump" or a loop of some kind, where the same copies of the variables are used each time. By using recursion, there is a new copy of every local variable each time it is called. This is true even of the "data" variable in the example, which never changes (hence, one inefficiency).

How to reverse the Stack?

May be it is too easy question but Please share how to reverse any kind of stack?
The right answer may be "don't reverse a stack."
If some constraint means you absolutely have to reverse a stack, your question is already answered. But, I can't help but wonder why you want to reverse a stack -- to me, that means you are using the wrong data structure.
If its all pushes, then the reverse, then all pops; thats a queue.
If pushes, pops, and reverses are mixed in random order, you probably want a data structure that specifically supports those operations... it will be confusing when someone reads "stack" but the structure is really something else.
Not sure about scala, but some languages have a double-ended queue to specifically represent a linear collection with access to the head and tail elements -- that may be exactly what you need. If not, a doubly-linked list will do nicely; or a plain old list will do if an O(n) pop()-equivalent isn't an issue.
If your pop() equivalent operation isn't aware of which end to get an element from (e.g., one piece of code is calling reverse, and some other code just says "give me an element" and isn't aware of whether or not reverse has been called), encapsulate the queue with a flag for direction as follows. This will give you the same functionality without having to actually reverse anything.
(sorry for the very-pseudo code, scala isn't in my toolbox).
ReversableStack {
DoublyLinkedList backingStore;
Boolean forward;
get() {
if (forward) return backingStore.take_head();
else return backingStore.take_tail();
}
put(item) {
if (forward) backingStore.add_head(item);
else backingStore.add_tail(item);
}
reverse() {
forward = !forward;
}
}
make new stack;
while (old stack not empty)
x = pop(old stack)
push x on new stack
Or call reverse on the stack, but note that that returns a List if applied to a mutable.Stack. It does return a Stack if you use immutable.Stack.
I don't speak Scala, but according to the doc you simply call reverse on the stack.
public void reverse(Stack st) {
int m = (int)st.Pop();
if (st.Count != 1)
reverse(st);
Push(st , m);
}
public void Push(Stack st , int a) {
int m = (int)st.Pop();
if (st.Count != 0)
Push(st , a);
else
st.Push(a);
st.Push(m);
}
Just loop around popping values from it and pushing them into another stack.

How to recognize what is, and what is not tail recursion?

Sometimes it's simple enough (if the self call is the last statement, it's tail recursion), but there are still cases that confuse me. A professor told me that "if there's no instruction to execute after the self-call, it's tail recursion". How about these examples (disregard the fact that they don't make much sense) :
a) This one should be tail recursive, seeing how the self-call is the last statement, and there's nothing left to execute after it.
function foo(n)
{
if(n == 0)
return 0;
else
return foo(n-2);
}
b) But how about this one? It should be a tail call, because if the condition is true, nothing except it will be executed, but it's not the last statement?
function foo(n)
{
if(n != 0)
return foo(n-2);
else
return 0;
}
c) How about this one? In both cases, the self call will be the last thing executed :
function foo(n)
{
if(n == 0)
return 0;
else
{
if(n > 100)
return foo(n - 2);
else
return foo(n - 1);
}
}
It might help you to think about this in terms of how tail-call optimisations are actually implemented. That's not part of the definition, of course, but it does motivate the definition.
Typically when a function is called, the calling code will store any register values that it will need later, on the stack. It will also store a return address, indicating the next instruction after the call. It will do whatever it needs to do to ensure that the stack pointer is set up correctly for the callee. Then it will jump to the target address[*] (in this case, the same function). On return, it knows the return value is in the place specified by the calling convention (register or stack slot).
For a tail call, the caller doesn't do this. It ignores any register values, because it knows it won't need them later. It sets up the stack pointer so that the callee will use the same stack the caller did, and it doesn't set itself up as the return address, it just jumps to the target address. Thus, the callee will overwrite the same stack region, it will put its return value in the same location that the caller would have put its return value, and when it returns, it will not return to its caller, but will return to its caller's caller.
Therefore, informally, a function is tail-recursive when it is possible for a tail call optimisation to occur, and when the target of the tail call is the function itself. The effect is more or less the same as if the function contained a loop, and instead of calling itself, the tail call jumps to the start of the loop. This means there must be no variables needed after the call (and indeed no "work to do", which in a language like C++ means nothing to be destructed), and the return value of the tail call must be returned by the caller.
This is all for simple/trivial tail-recursion. There are transformations that can be used to make something tail-recursive which isn't already, for example introducing extra parameters, that store some information used by the "bottom-most" level of recursion, to do work that would otherwise be done on the "way out". So for instance:
int triangle(int n) {
if (n == 0) return 0;
return n + triangle(n-1);
}
can be made tail-recursive, either by the programmer or automatically by a smart enough compiler, like this:
int triangle(int n, int accumulator = 0) {
if (n == 0) return accumulator;
return triangle(n-1, accumulator + n);
}
Therefore, the former function might be described as "tail recursive" by someone who's talking about a smart enough language/compiler. Be prepared for that variant usage.
[*] Storing a return address, moving the stack pointer, and jumping, may or may not be wrapped up in a single opcode by the architecture, but even if not that's typically what happens.
All your functions are tail recursive.
no instruction left after the self-call
means: After the self-call, you return from the function, i.e. no more code has to be executed, and not that there is no more line of code in the function.
Yep; I think your professor meant that in any path, if the final instruction is recursive, then it is tail recursion.
So, all three examples are tail-recursive.
All three examples are tail recursive. Generally speaking, it is tail recursion, if the result of the function (the expression following the "return" keyword) is a lone call to the function itself. No other operator must be involved in the outermost level of the expression. If the call to itself is only a part of an expression then the machine must execute the call but then has to return back into the evaluation of said expression, that is, it was not at the tail of the function execution but in the middle of an expression. This however does not apply to any parameters that the recursive call may take: anything is allowed there, including recursive calls to itself (e.g. "return foo(foo(0));"). The optimization of calls to jumps is only possible for the outer call then, of course.

An efficient technique to replace an occurence in a sequence with mutable or immutable state

I am searching for an efficient a technique to find a sequence of Op occurences in a Seq[Op]. Once an occurence is found, I want to replace the occurence with a defined replacement and run the same search again until the list stops changing.
Scenario:
I have three types of Op case classes. Pop() extends Op, Push() extends Op and Nop() extends Op. I want to replace the occurence of Push(), Pop() with Nop(). Basically the code could look like seq.replace(Push() ~ Pop() ~> Nop()).
Problem:
Now that I call seq.replace(...) I will have to search in the sequence for an occurence of Push(), Pop(). So far so good. I find the occurence. But now I will have to splice the occurence form the list and insert the replacement.
Now there are two options. My list could be mutable or immutable. If I use an immutable list I am scared regarding performance because those sequences are usually 500+ elements in size. If I replace a lot of occurences like A ~ B ~ C ~> D ~ E I will create a lot of new objects If I am not mistaken. However I could also use a mutable sequence like ListBuffer[Op].
Basically from a linked-list background I would just do some pointer-bending and after a total of four operations I am done with the replacement without creating new objects. That is why I am now concerned about performance. Especially since this is a performance-critical operation for me.
Question:
How would you implement the replace() method in a Scala fashion and what kind of data structure would you use keeping in mind that this is a performance-critical operation?
I am happy with answers that point me in the right direction or pseudo code. No need to write a full replace method.
Thank you.
Ok, some considerations to be made. First, recall that, on lists, tail does not create objects, and prepending (::) only creates one object for each prepended element. That's pretty much as good as you can get, generally speaking.
One way of doing this would be this:
def myReplace(input: List[Op], pattern: List[Op], replacement: List[Op]) = {
// This function should be part of an KMP algorithm instead, for performance
def compare(pattern: List[Op], list: List[Op]): Boolean = (pattern, list) match {
case (x :: xs, y :: ys) if x == y => compare(xs, ys)
case (Nil, Nil) => true
case _ => false
}
var processed: List[Op] = Nil
var unprocessed: List[Op] = input
val patternLength = pattern.length
val reversedReplacement = replacement.reverse
// Do this until we finish processing the whole sequence
while (unprocessed.nonEmpty) {
// This inside algorithm would be better if replaced by KMP
// Quickly process non-matching sequences
while (unprocessed.nonEmpty && unprocessed.head != pattern.head) {
processed ::= unprocessed.head
unprocessed = unprocessed.tail
}
if (unprocessed.nonEmpty) {
if (compare(pattern, unprocessed)) {
processed :::= reversedReplacement
unprocessed = unprocessed drop patternLength
} else {
processed ::= unprocessed.head
unprocessed = unprocessed.tail
}
}
}
processed.reverse
}
You may gain speed by using KMP, particularly if the pattern searched for is long.
Now, what is the problem with this algorithm? The problem is that it won't test if the replaced pattern causes a match before that position. For instance, if I replace ACB with C, and I have an input AACBB, then the result of this algorithm will be ACB instead of C.
To avoid this problem, you should create a backtrack. First, you check at which position in your pattern the replacement may happen:
val positionOfReplacement = pattern.indexOfSlice(replacement)
Then, you modify the replacement part of the algorithm this:
if (compare(pattern, unprocessed)) {
if (positionOfReplacement > 0) {
unprocessed :::= replacement
unprocessed :::= processed take positionOfReplacement
processed = processed drop positionOfReplacement
} else {
processed :::= reversedReplacement
unprocessed = unprocessed drop patternLength
}
} else {
This will backtrack enough to solve the problem.
This algorithm won't deal efficiently, however, with multiply patterns at the same time, which I guess is where you are going. For that, you'll probably need some adaptation of KMP, to do it efficiently, or, otherwise, use a DFA to control possible matchings. It gets even worse if you want to match both AB and ABC.
In practice, the full blow problem is equivalent to regex match & replace, where the replace is a function of the match. Which means, of course, you may want to start looking into regex algorithms.
EDIT
I was forgetting to complete my reasoning. If that technique doesn't work for some reason, then my advice is going with an immutable tree-based vector. Tree-based vectors enable replacement of partial sequences with low amount of copying.
And if that doesn't do, then the solution is doubly linked lists. And pick one from a library with slice replacement -- otherwise you may end up spending way too much time debugging a known but tricky algorithm.

Resources