0

I'm learning how to write recursions, and I am confused as to how to simplify the body of a function into a recursion.

For my current assignment, I have "to Mesh two strings by alternating characters from them. If one string runs out before the other, just pick from the longer one. For example, mesh("Fred", "Wilma") is "FWrieldma". Use recursion. Do not use loops."

Well... I created the loop....

string result;
for (int i = 0; i < a.size(); ++i)
{
    result += a.at(i) + b.at(i)
}

But making that into a recursion is stumping me.

This is what I have so far (We are not allowed to change anything above or below where it is marked):

#include <string>
using namespace std;

/**
   Combines alternate characters from each string.
   @param a the first string.
   @param b the second string
*/
string mesh(string a, string b)
{
// INSERT CODE BENEATH HERE
     string result;
     if (a.size() < 1) result = b;
     if (b.size() < 1) result = a;
     else
     {
        result = a.at(0) + b.at(1);
     }
return result;
// ENTER CODE ABOVE HERE
}

But i know its not right because there is no recursion and it flat out doesn't work

Michele
  • 33
  • 6
  • 1
    The easiest way to think of recursion is that you're going to do something for the first element of a set of data (so the first character of a string, for example), then you're going to do it again to the remainder of the data. Break up the problem like that- first get it working for 1 element, then find a way to call it on the rest of the data. – Gabe Sechan Mar 16 '21 at 18:19

5 Answers5

4

I think this does what you've asked and keeps the function prototype intact. Also it looks similar to your suggested code.

#include <iostream>

using namespace std;

string mesh(string a, string b) {
    if (!a.size()) return b;
    if (!b.size()) return a;
    return a[0] + (b[0] + mesh(a.substr(1), b.substr(1)));
}

int main(int argc, char const *argv[])
{
    printf("%s\n", mesh("Fred", "Wilma").c_str());
    return 0;
}
  • the other problem was also trying avoid a performance hit with many constructor calls. – masonCherry Mar 16 '21 at 18:49
  • 2
    I don't think OP cares about that given the task of writing it using recursion(assuming an useful use of recursion for the problem) and not changing the function signature. – Andrei Pangratie Mar 16 '21 at 18:53
1

First try to find out what is a single step of the recursion. There is more than one way to do it, one possibility is traverse the strings by using some index pos and in a single step add the characters from the respective positions of the strings:

std::string mesh(const std::string& a, const std::string& b,size_t pos) {

    /*...*/

    std::string result;
    if (pos < a.size()) result += a[pos];
    if (pos < b.size()) result += b[pos];

    /*...*/
}

To recurse we call the method again for the next index and append to result:

std::string mesh(const std::string& a, const std::string& b,size_t pos = 0) {

    /*...*/
    
    std::string result;
    if (pos < a.size()) result += a[pos];
    if (pos < b.size()) result += b[pos];

    return result + mesh(a,b,pos+1);
}

Finally we need a stop condition. The recursion should stop when both strings have no more characters at index pos:

std::string mesh(const std::string& a, const std::string& b,size_t pos = 0) {

    if (pos >= a.size() && pos >= b.size()) return "";
    
    std::string result;
    if (pos < a.size()) result += a[pos];
    if (pos < b.size()) result += b[pos];

    return result + mesh(a,b,pos+1);
}

For example:

int main() {
    std::cout << mesh("Fred","Wilma");
}

will result in the desired FWrieldma output.

Disclaimer: As pointed out by SergeyA, I didn't pay to much attention to performance when writing this answer. I suppose this is an exercise to practice recursion, while in reality I don't see a reason to implement this via recursion.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • This is correct from algo point of view, but terrible from performance point of view. All those copies of strings which would be made... It is also not TCO friendly. – SergeyA Mar 16 '21 at 18:25
  • @SergeyA ups, forgot references, though even without the copies this might cause a stackoverflow for long strings when a loop is much simpler and has no such issues – 463035818_is_not_an_ai Mar 16 '21 at 18:27
  • Yes, and this is what I meant by non-TCO friendly. – SergeyA Mar 16 '21 at 18:31
  • All my teacher did to teach us was upload a basic textbook page and then give us a slew of confusing exercises without really taking a moment to explain anything. For this exercise, we aren't allowed to add another parameter, is there a way around that? – Michele Mar 16 '21 at 18:32
  • why wouldn't you be allowed to add another parameter. that just seems silly imo. – masonCherry Mar 16 '21 at 18:34
  • @SergeyA I have to admit, that I first have to read a bit about tco. Can you give me a pointer as to what exactly prevents TCO here? Though if OP isnt allowed to change signature, I can only think of solutions that are even worse – 463035818_is_not_an_ai Mar 16 '21 at 18:34
  • I physcially can't. He gives us the outside code in a grey box, and then in the middle is a white box we're supposed to type our code into. We literally can't change anything outside of the white box, and the given parameters are outside the white box – Michele Mar 16 '21 at 18:35
  • @Michele yes there is a way around that. I'll need some time to update the answer. Please mention such constraints also in your question – 463035818_is_not_an_ai Mar 16 '21 at 18:35
  • I am not sure if there is a good comprehensive read on TCO constraints (my knowledge comes from personal experience and experiments), but local objects (variables or parameters) with non-trivial destructors would certainly prevent TCO. The first step would be (although it violate OP constraints) to make sure there are no local `std::string`, and all inputs are either references or pointers to chars. Return value will have to become an out parameter, passed by reference as well (or another pointer to char). – SergeyA Mar 16 '21 at 18:40
  • TCO == Total Cost of Ownership? Too many TLAs – Chris Dodd Mar 16 '21 at 18:41
  • @SergeyA thanks a lot! Meanwhile I found this: https://stackoverflow.com/a/35649259/4117728, which is also about destrcutors. Unfortunately given the constraints one not only has to make copies but even worse create substrings – 463035818_is_not_an_ai Mar 16 '21 at 18:42
  • 1
    @ChrisDodd Tail Call Optimization – masonCherry Mar 16 '21 at 18:42
  • @largest_prime_is_463035818 I've added to your answer to keep the signature of `mesh` function the same as @Michele claims he has to. – masonCherry Mar 16 '21 at 18:46
  • @Michele I updated the code shared by @largest_prime_is_463035818 and remove the `pos` parameter. Have a look from [here](https://onlinegdb.com/Hk753dA7_). – biqarboy Mar 16 '21 at 18:47
  • @biqarboy that worked! I won't claim to fully understand it yet... but I'm gonna study it and continue practicing until I hopefully do. – Michele Mar 16 '21 at 18:50
  • @MrBrN197 maybe you are too new to SO to know about the "pronouns escalation". Not that I care personally much, but using "they" is the better alternative when in doubt – 463035818_is_not_an_ai Mar 16 '21 at 18:51
  • @biqarboy please do not post answers in comments when you can post it as an answer – 463035818_is_not_an_ai Mar 16 '21 at 18:52
  • Okay, I will post it as a separate answer with proper explanation. Thanks for your comment @largest_prime_is_463035818 – biqarboy Mar 16 '21 at 18:54
  • 1
    Thank you all for your help! – Michele Mar 16 '21 at 18:55
  • @Michele i was out of ideas and only found a way to keep the signature by looking at other answers, but its pointless to copy others solutions in to my answer, so I will just leave it be. – 463035818_is_not_an_ai Mar 16 '21 at 18:58
  • @Michele just note that it cannot be emphasized enough that this exercise really is just about recursion, not about writing code you would write in reality. As Sergey pointed out, my solution is not tail call optimization friendly but I wouldn't know how to fix that given your constraints. Also, depends on your preference, but imho recursion is almost never "simpler" than the iterative solution. – 463035818_is_not_an_ai Mar 16 '21 at 19:00
  • @largest_prime_is_463035818 I actually assumed Michele was a she but when typing I just probably temporarily forgot and typed he. – masonCherry Mar 16 '21 at 19:06
  • 1
    @MrBrN197 Don't get me wrong, I am not the he/she police ;). Using "they" is a nice way to not be wrong always. – 463035818_is_not_an_ai Mar 16 '21 at 19:09
0

Just adding onto largest_prime_is_463035's answer. If you have to keep signature of mesh the same then you would create another function that has the actual implementation and now mesh can be called be only the two string arguments.

#include <string>
#include <iostream>
using namespace std;

/**
   Combines alternate characters from each string.
   @param a the first string.
   @param b the second string
*/

void meshInternal(const string a, const string b, string& result, unsigned int index=0){
    if(index >= a.size()){
        result += b.substr(index);
        return;
    }
    if(index >= b.size()){
        result += a.substr(index);
        return;
    }

    result.push_back(a[index]);
    result.push_back(b[index]);
    meshInternal(a, b, result, ++index);
}

string mesh(const string a, const string b)
{
    string result = "";
    meshInternal("Fred", "Wilma", result);
    return result;
}

int main() {
    string result = mesh("Fred", "Wilma");
    std::cout << result << std::endl;
    return 2;
}
masonCherry
  • 894
  • 6
  • 14
0

As it is not possible to pass another parameter in the mesh function, but in every recursive call we need to know which character from string a and string b will be appended to the result. One simple solution may be removing the first character from both string a and string b and append it to the result. Now, as we are passing string a and string b as reference, removing first character will ultimately make the string empty after a while. So, we can check whether both the string a and string b become empty and set it as the base-case of the recursion call.

This code solves the problem:

std::string mesh(string& a, string& b) {

    if (a.size() == 0 && b.size() == 0) return "";
    
    std::string result;
    if (a.size()) {
        result += a[0];
        a.erase(0, 1);
    }
    if (b.size()) {
        result += b[0];
        b.erase(0, 1);
    }

    return result + mesh(a,b);
}

int main()
{
    string a = "Fred";
    string b = "Wilma";
    std::cout << mesh(a,b);

    return 0;
}
biqarboy
  • 852
  • 6
  • 15
  • Could be more efficient. As soon as one parameter reaches zero length simply return the other as the result. If you do that you no longer need to check the size of the parameters before checking because we will know that both will always be greater than zero size. – Martin York Mar 16 '21 at 19:10
  • Yes, that would possible. – biqarboy Mar 16 '21 at 19:11
0
#include <string>
#include <iostream>
#include <string_view>

// recursive mesh function.
// passing the result object for effeciency.
void mesh(std::string& result, std::string_view l, std::string_view r)
{
    // check the exit condition.
    // If either the left of right are empty add the other to the result.
    if (std::begin(l) == std::end(l)) {
        result += r;
        return;
    }
    if (std::begin(r) == std::end(r)) {
        result += l;
        return;
    }

    // Add letter from the left and right to the result.
    result += *std::begin(l);
    result += *std::begin(r);

    // Adjust the size of the view
    l.remove_prefix(1);
    r.remove_prefix(1);

    // recursively call to get the next letter.
    mesh(result, l, r);
}

// Utility wrapper to get view of strings and create
// the result object to be passed to the recursive function.
std::string mesh(std::string const& l, std::string const& r)
{
    std::string result;
    mesh(result, std::string_view(l), std::string_view(r));
    return result;
}

int main()
{
    std::cout << mesh("Fred", "Wilma");
}
Martin York
  • 257,169
  • 86
  • 333
  • 562