4
\$\begingroup\$

My daughter, nine, recently had some homework looking at number palindromes. Part of the assignment was to take a number, reverse it, add the two together and repeat until she got a palindrome and to do this for several different numbers.

While helping her with this it struck me it'd be a nice simple idea for a computer program. She & I have messed about in Scratch a bit, but this sounded like a nice problem to do in a more commercial language. Thought I could write it and then show her how it worked.

So I did. It turned out to be not quite as child-friendly as I'd imagined.

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

namespace CheckNumberPalindrome
{
    class Program
    {
        static void Main(string[] args)
        {
            string rollingSum;
            int iterations = 1;

            if(IsNumeric(args[0]) == false)
            {
                Console.WriteLine("You did not enter a numeric value");
            }

            rollingSum = RecurseUntilPalindrome(args[0], ref iterations);
            Console.WriteLine(string.Format("The final palindrome is {0}, which took {1} steps.", rollingSum, iterations));
        }

        private static bool IsNumeric(string s)
        {
            long result;
            return Int64.TryParse(s, out result);
        }

        private static string RecurseUntilPalindrome(string s, ref int iterations)
        {
            long rollingSum = AddToPalindrome(s);

            if(IsPalindrome(rollingSum.ToString()))
            {
                return rollingSum.ToString();
            }

            iterations++;
            return RecurseUntilPalindrome(rollingSum.ToString(), ref iterations);
        }

        private static long AddToPalindrome(string s)
        {
            string reversedString = ReverseString(s);
            long result = Convert.ToInt64(s) + Convert.ToInt64(reversedString);
            Console.WriteLine(string.Format("Adding {0} to {1} = {2}", s, reversedString, result));
            return result;
        }

        private static string ReverseString(string s)
        {
            char[] charArray = s.ToCharArray();
            Array.Reverse(charArray);
            return new string(charArray);
        }

        private static bool IsPalindrome(string s)
        {
            return s.SequenceEqual(ReverseString(s));
        }
    }
}

There are two questions here:

  1. How could I make this better or cleaner or more efficient or any of the other things that CR does so well?

  2. What could I do (beside hiding some of the functions in pre-compiled code elsewhere) to make this more child-friendly, or better for teaching/explaining some basic code concepts?

\$\endgroup\$
  • 2
    \$\begingroup\$ FWIW, Console.WriteLine has an overload that takes a format string and an array of objects so you don't need to use string.Format. \$\endgroup\$ – RobH Mar 16 '15 at 14:38
3
\$\begingroup\$

Overall your code looks very nice, readable and maintainable. I only have a few remarks:

  • Input validation:
if(IsNumeric(args[0]) == false)
 {
     Console.WriteLine("You did not enter a numeric value");
 }
 rollingSum = RecurseUntilPalindrome(args[0], ref iterations);

Why continue the program when the input is invalid (not numeric)? Check for a numeric value and only continue if the input is a number:

if(IsNumeric(args[0]))
{
    //Continue here...
}
else
{
    Console.WriteLine("You didn't enter a valid number!");
}
  • Format in Console.WriteLine:

Following line:

Console.WriteLine(string.Format("The final palindrome is {0}, which took {1} steps.", rollingSum, iterations));

can be rewritten as:

Console.WriteLine("The final palindrome is {0}, which took {1} steps.", rollingSum, iterations);

No need to call the String.Format.

  • Check for palindrome:

While writing previous points I found that there had to be an easier way to achieve the same result. There are numerous ways to check for a palindrome. For numbers you can also use this method:

public static bool IsPalindrome(Int64 input)
{
    Int64 reverse = 0;
    var temp = input;

    while (temp != 0)
    {
        var rem = temp % 10;
        reverse = reverse * 10 + rem;
        temp /= 10;
    }

    return reverse == input;
}

When using this method, there's no need for creating strings and arrays and again conversions to numbers. Your entire code can be written as follows:

Int64 startNumber;
var steps = 1;
var input = args[0];

if (Int64.TryParse(input, out startNumber))
{
    Int64 reversed;

    while(!IsPalindrome(startNumber, out reversed))
    {
        steps++;
        var temp = startNumber + reversed;
        Console.WriteLine("Adding {0} to {1} = {2}", reversed, startNumber, temp);
        startNumber = temp;
    }

    Console.WriteLine("The final palindrome is {0}, which took {1} steps.", startNumber, steps);
}
else
{
    Console.WriteLine("You didn't enter a valid number!");
}
\$\endgroup\$
  • \$\begingroup\$ Nice, thanks. I went with comparing against a reverse string because I thought it would be easier to explain than how the maths works. Pretty silly of me to do recursion rather than a while loop, though - the latter is much easier to explain, too! \$\endgroup\$ – Matt Thrower Mar 16 '15 at 16:18
  • 1
    \$\begingroup\$ It's not illogical to use recursion but I try to avoid it when a simple loop can solve the problem. And I understand that 'reversing a string' is easier to explain to a 9-year old than the piece of math. Kudos for your daughter though for trying/willing to understand this matter! :) \$\endgroup\$ – Abbas Mar 16 '15 at 20:27
3
\$\begingroup\$

This may help with your second point about code concepts:

How about addressing the special case for when the user enters a palindrome? It looks to me like you are assuming the entered number will not be a palindrome and you automatically add to it (calling RecurseUntilPalindrome). How about adding an initial check to account for that case? I think it would go along well with your numerical value input check (I consider invalid user input as a sort of special case as well).

To address your first point, I think your code looks pretty clean as is! Really easy to follow.

\$\endgroup\$
  • \$\begingroup\$ Thanks. A good thought - perhaps I'll take her through the code and then, if she's interested, help her put together the extra validation you're suggesting. \$\endgroup\$ – Matt Thrower Mar 16 '15 at 14:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.