-2

Is there a way to assign an int into a string with a recursive function without reversing the number twice with each recursive call?

#include <string.h>
#include <iostream>

using namespace std;

int numreverse(long int n, long int rev);
void numtostring(unsigned int n, char str[]);

int main()
{
    char n[5];
    numtostring(1234, n);
    cout << n << endl;
}

void numtostring(unsigned int n, char str[]){

    n = numreverse(n, 0);//reverse the int

    if (n <= 0)return;
    else{
        str[0] = (char)(n % 10 + (int)'0');
        str[1] = 0;
        n /= 10;
        n = numreverse(n, 0);//reverse it again before recursive call
        numtostring(n, str + 1);
    }
}

int numreverse(long int n, long int rev){
    if (n == 0)
        return rev;
    return numreverse(n / 10, rev * 10 + n % 10);
}

Note: numtostring itself has to be recursive and its signature cannot be modified.

Note2: no additional libraries can be used.

Note3: static or global variables should be avoided.

shinzou
  • 5,850
  • 10
  • 60
  • 124
  • 1
    It would be easier if you used `std::string` instead of `char[]`. – Barmar Dec 27 '14 at 08:56
  • `(int)'0'` is pointless because all types less than int must be promoted to int (or unsigned int if `sizeof(char) == sizeof(int)`) anyway – phuclv Dec 27 '14 at 08:57
  • 1
    Why don't you build your string and reverse the string once in the end? I mean, convert the int 1234 into the string "4321" recursively, and then reverse it once to obtain "1234". – sandris Dec 27 '14 at 08:59
  • @Barmar to be honest, I haven't learned how to use `std::string` yet... – shinzou Dec 27 '14 at 08:59
  • @sandris because the function itself has to do it without relying on the number to be reversed when the function is called. – shinzou Dec 27 '14 at 09:00
  • Do you mean converting the int value to a string? Why don't just use the functions provided by the standard? And if you want to learn the implementation then there are tons of them on SO either http://stackoverflow.com/questions/8257714/how-to-convert-an-int-to-string-in-c http://stackoverflow.com/questions/5590381/easiest-way-to-convert-int-to-string-in-c http://stackoverflow.com/questions/7663709/convert-string-to-int-c – phuclv Dec 27 '14 at 09:02
  • @LưuVĩnhPhúc because I have to do it without relying on given functions. – shinzou Dec 27 '14 at 09:03
  • 1
    `str[0] = n % 10 + '0';` is enough – phuclv Dec 27 '14 at 09:03
  • The links I gave above already show the ways to convert without standard library, did you read it? – phuclv Dec 27 '14 at 09:04
  • @LưuVĩnhPhúc non of the answers are a recursive function. – shinzou Dec 27 '14 at 09:06
  • 2
    Yes, there are tons of ways to do it without reversing the number. All else apart, reversing the number is error prone. Reversing 1000 twice doesn't give you 1000 as a result. `int numreverse(long int n, long int rev){ if (n == 0) return rev; return numreverse(n / 10, rev * 10 + n % 10); } int main(void) { y = numreverse(1000, 0); int x = numreverse(y, 0); printf("x = %d, y = %d, z = %d\n", x, y, z); return 0; }` – Jonathan Leffler Dec 27 '14 at 09:09
  • you should use #include instead of #include – user2393256 Dec 27 '14 at 09:30
  • @JonathanLeffler it looks like I didn't check that case but apparently I was able to solve it with two conditionals and a boolean http://pastebin.com/rD4XPWJx anyway, I'd appreciate if you could share a way to do this without reversing the number if you can. – shinzou Dec 27 '14 at 09:44
  • Well I take it back, this doesn't solve `1002`... – shinzou Dec 27 '14 at 10:11
  • If recursion is a must why don't you just reverse the string after finishing like sandris said? – phuclv Dec 27 '14 at 10:26
  • @LưuVĩnhPhúc because the function has to do everything, if I'll add a call a function that will reverse the string at the end of `numtostring` then it will reverse the string at the end of each recursion which would be too many reverses... – shinzou Dec 27 '14 at 10:32
  • What prevents you from writing `numtostring` as a wrapper that calls another function that does the real conversion? The reversion can then be done in `numtostring` – phuclv Dec 27 '14 at 12:52
  • @LưuVĩnhPhúc because `numtostring` itself has to the recursive function, not a wrapper for another recursive function... – shinzou Dec 27 '14 at 12:55
  • @kuhaku Whoever gave you that silly requirement should probably be fired. – fredoverflow Dec 27 '14 at 20:36
  • @FredOverflow lol think of it like this, you can't explain a 6th grade kid how solve an equation using Taylor expansion and L'Hopital rule, I'm basically a kid in terms of programing. – shinzou Dec 27 '14 at 20:42

3 Answers3

2

The interface to numtostring() is a C-like interface. Apart from the choice of I/O operations in the main() test program shown in the question, the code could perfectly well be C instead of C++.

The requirement not to change the interface to numtostring() and for it to be recursive makes it messier and less efficient than it should be, doubly so if you're not allowed to use other library functions. In C++, you really shouldn't be mucking around with raw arrays of char and the like either. However, rules are rules. The code can be implemented without the numreverse() function — which fails when used to re-reverse numbers that end with one or more zeros (for example, 1000 reversed is 1, but 1 reversed is still 1, not 1000, or was that 100, 10, or even 1,000,000,000?).

The key trick with the recursion is to ensure that you have the base case identified and handled properly, and then that the recursive case builds on the base case. Here, the base case needs to be 'when the value is less than 10' and the recursive case (value at least 10) needs to get the string for the leading digits (corresponding to the value divided by 10) and then append a final digit.

What makes it tricky is that the function can't assume anything about str except 'the pointer is to modifiable memory' and 'there is enough space to hold the number being formatted plus a terminal null' when it is first called. In particular, it can't assume that str has a null byte at the beginning. So, the base case must ensure that the string is null terminated; the recursive case must append to the 'known to be null terminated' string. This means that the code runs slower than it might with less stringent constraints because the recursive case has to find the end of the string to append its digits.

#include <iostream>
#include <cstring>  // memset() - used in test code

using namespace std;

void numtostring(unsigned int n, char str[]);

int main()
{
    int v;
    while (cin >> v)
    {
        char n[32];
        memset(n, '\xA5', sizeof(n));
        numtostring(v, n);
        cout << v << " == [" << n << "]\n";
    }
}

void numtostring(unsigned int n, char str[])
{
    if (n > 9)
    {
        numtostring(n / 10, str);
        while (*str != '\0')
            str++;
        *str++ = n % 10 + '0';
        *str = '\0';
    }
    else
    {
        *str++ = n % 10 + '0';
        *str = '\0';
    }
}

Sample run:

$ n2s < data.n2s
0 == [0]
1 == [1]
2 == [2]
3 == [3]
4 == [4]
5 == [5]
6 == [6]
7 == [7]
8 == [8]
9 == [9]
10 == [10]
11 == [11]
99 == [99]
100 == [100]
999 == [999]
1000 == [1000]
1001 == [1001]
1002 == [1002]
$

(The program source was in n2s.cpp and was compiled into program n2s, and the numbers to be converted were stored in data.n2s. The square brackets, and the memset() operation, simply ensure that the data is properly set by the formatting function.)

Code review helps

There's an ugly repetition in numtostring() above, and a little more thought shows that it is not only ugly but also unnecessary. A more streamlined version of the code is:

void numtostring(unsigned int n, char str[])
{
    if (n > 9)
    {
        numtostring(n / 10, str);
        while (*str != '\0')
            str++;
    }
    *str++ = n % 10 + '0';
    *str = '\0';
}

The while loop is a nuisance but necessary.

Using a helper function

Since numreverse() was allowed, it seems clear that the code could use functions that were written as part of the solution, so a more efficient solution uses a recursive helper function, but this solution also contravenes the rules because numreverse() is no longer itself recursive.

#include <iostream>
#include <cstring>  // memset() - used in test code

using namespace std;

void numtostring(unsigned int n, char str[]);

int main()
{
    int v;
    while (cin >> v)
    {
        char n[32];
        memset(n, '\xA5', sizeof(n));
        numtostring(v, n);
        cout << v << " == [" << n << "]\n";
    }
}

static char *n2s_helper(unsigned int n, char *str)
{
    if (n > 9)
        str = n2s_helper(n / 10, str);
    *str = n % 10 + '0';
    return str + 1;
}

void numtostring(unsigned int n, char str[])
{
    str = n2s_helper(n, str);
    *str = '\0';
}

For the same input, it produces the same output. It is more efficient because it only writes one null byte into the result string, and because it doesn't need to find the end of the string each time it needs to add a digit. It would take a lot of conversions to spot the difference in performance, of course.

In both solutions, the operational code (numtostring() and n2s_helper()) are valid C functions, just as they are valid C++ functions, and their behaviour is the same in either language.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Wow very nice. About the first piece of code, does it have to call `*str` by pointer? – shinzou Dec 27 '14 at 17:25
  • I'm not sure what you mean. In a function argument list, `char str[]` is treated by the compiler as though you wrote `char *str`, and I normally write that in my own code (witness `n2s_helper()`), but I left your interface unchanged. Any pointer `ptr` can be dereferenced using `*ptr` or `ptr[0]` (or, obfuscatedly, `0[ptr]`). This duality between pointers and arrays is a common cause of confusion, not least because there are major differences between pointers and arrays, even though there are also a lot of similarities. If you're asking about something else, please clarify what you mean. – Jonathan Leffler Dec 27 '14 at 17:28
  • I'm kinda new to the dereferencing operator so I just checked and the code works if I change `*str` to `str[i]` etc, it was easier for me to understand it with this. It blew me away with its simplicity. – shinzou Dec 27 '14 at 20:10
0

Finally did it! (EDIT - OP edited question, static and global vars not to be used)

#include <iostream>
using namespace std;

void numtostring(unsigned int n, char str[]) //no change made to the functionn signature
{
    char* count = str;
    static int counter = 0; //static integer does the trick
    char temp = 0;
    if (n)
    {
        temp = n%10 + '0';
        numtostring(n/10, count);
    }
    *(count+counter) = temp;
    if (temp) count[++counter] = '\0';
    else counter = 0;
}

int main()
{
    char n[5] = "TECH";
    numtostring(12, n);
    cout << n << endl;
    numtostring(32, n);
    cout << n << endl;
}
technusm1
  • 503
  • 5
  • 14
  • `if(temp) counter++`?!? I wonder what will happen if you try to print `101` with such code. – Alexis Wilke Dec 27 '14 at 10:27
  • 1
    Please note that if your function is called more than once, `counter` would never be reset to 0, so you'd be writing to the wrong places after the first non-recursive call to the function completes. Using a static variable also means the code is not reentrant; it cannot be used in a threaded application. Occasionally, static variables are the way to do something; this is not one of those occasions. When I converted your `main()` to a loop (as in my answer), I got the output: `0 == []` (oops) `1 == [1] 2 == [12] 3 == [123] 4 == [1234] 5 == [12345] 6 == [123456]` etc, and eventually a seg fault. – Jonathan Leffler Dec 27 '14 at 17:40
  • i fixed the multiple calls problem. SILLY BUG on my part. i'll see if i can work on the other issue @JonathanLeffler – technusm1 Dec 27 '14 at 17:59
  • i was trying to avoid the helper function, like reverse and all, but seems like it can't be done unless i violate one of those 3 conditions – technusm1 Dec 27 '14 at 18:07
  • When I ran your revised code, I got `12CH` as the first output. I was able to fix that by changing the last `if/else` statement in the function to: `if (temp) { counter++; count[counter] = '\0'; } else counter = 0;`, which can be abbreviated to: `if (temp) count[++counter] = '\0'; else counter = 0;`, of course. – Jonathan Leffler Dec 27 '14 at 19:31
  • made changes as you suggested @JonathanLeffler – technusm1 Dec 28 '14 at 03:22
0

Note: numtostring itself has to be recursive and its signature cannot be modified.

That's a silly requirement. Can I at least change the return type? Makes the code so much simpler:

char * numtostring(unsigned int n, char * str)
{
    if (n >= 10) str = numtostring(n / 10, str);
    *str = n % 10 + '0';
    *++str = 0;
    return str;
}
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • Yeah I know it's silly but that's how I'm supposed to learn I guess... I did manage to solve it for all cases and it's disgusting, multiple loops, reverse `n` twice, terrible... BTW, when I try to run your code like so: `char n[10]; numtostring(1003050, n);` it returns: `1003050╠╠╠╠╠╠╠╠╠QS6≥D∙╟` – shinzou Dec 27 '14 at 12:57
  • That's because you did not NUL-terminate the string. Try `char n[10] = {0};` in `main`. – fredoverflow Dec 27 '14 at 13:01
  • 1
    @FredOverflow: it is not clear that you can assume anything about `str` except 'the pointer is to modifiable memory' and 'there is enough space to hold the number being formatted plus a terminal null'. The constraints on the function postulated by the OP are ridiculous; it would be a lot easier if you could have a wrapper function to initiate the recursion and a simpler function to handle the recursion. As it stands, your function could end: `*str++ = n % 10 + '0'; *str = '\0'; return str;` which writes more nulls than you'd like but otherwise meets the goals — the output is null terminated. – Jonathan Leffler Dec 27 '14 at 16:29
  • @JonathanLeffler I didn't realize the original function did exactly that. Thanks for the hint. – fredoverflow Dec 27 '14 at 20:33