HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpMinor

Create a chain of unique integers from single integer

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
uniquecreateintegerssinglefromintegerchain

Problem

I've done a practice session on cyber-dojo and would like someone to review it.

Main question - is my solution too complex for such a simple task?

For example, I decided to split out printing and generating logic into ChainPrinter and NumberGenerator classes. This in my opinion conforms better with Single Responsibility and Open Closed Principles, but I can't get rid of a feeling that I've over complicated it.

Feel free to comment on other aspects of solution as well.

Instructions:


Given a number, we can form a number chain by



  • arranging its digits in descending order



  • arranging its digits in ascending order



  • subtracting the number obtained in (2) from the number


obtained (1) to form a new number

  • and repeat these steps unless the new number has already


appeared in the chain



Note that 0 is a permitted digit. The number of distinct numbers in
the chain is the length of the chain. You are to write a program that
reads numbers and outputs the number chain and the length of that
chain for each number read.

Input and Output


The input consists of a positive number, less than 10^9. The output consists of the number chain generated by the input number, followed
by its lengths exactly in the format indicated below.

Examples


Example-1


Input

123456789




Output

Original number was 123456789
987654321 - 123456789 = 864197532
987654321 - 123456789 = 864197532
Chain length 2




Example-2


Input

1234




Output

Original number was 1234
4321 - 1234 = 3087
8730 - 378 = 8352
8532 - 2358 = 6174
7641 - 1467 = 6174
Chain length 4




Example-3


Input

444




Output

Original number was 444
444 - 444 = 0
0 - 0 = 0
Chain length 2


Solution:

```
using System.Collections.Generic;
using System.Linq;

public struct NumberChainItem
{
public int Number { get; set; }
public string GenerationDescription { get; set; }
}

public cla

Solution

Yes I think your design is over-complicated. But more to the point, your implementation is wrong. The spec says


repeat these steps unless the new number has already appeared in the chain

But what you implement is "repeat these steps unless the new number is equal to the previous number". That's wrong.

Why did you get this wrong? You didn't create a method that clearly implements the specification, that's why.

When I see a problem like this I try to come up with a collection of methods where each exactly implements some part of the specification, in a general way. I would have implemented that line of the spec like this:

public static IEnumerable UntilDuplicate(
  this IEnumerable items, IEqualityComparer equals) 
{
  var seen = new HashSet(equals);
  foreach(var item in items)
  {
    if (seen.Contains(item)) 
      yield break;
    seen.Add(item);
    yield return item;
  }
}


Easy method. Obviously correct. And when you use it in your solution, that solution will be seen to be obviously correct because it clearly and correctly implements an idea from the specification. Programming is about correctly solving small problems and combining those solutions together.

If you build up a small collection of helper methods like this you will very quickly discover that you can write the program you want to write so that it reads very close to its specification, and this gives you confidence that it is both correct and concise.


When I was solving the problem, a pluggable INextNumberGenerator seemed like a good idea. Would you see this line of thinking as 'over-engineering'?

It's not a bad idea to encapsulate that logic somewhere. But the question to ask when you're consider an interface is: do I need this generality? Am I really going to make two, three, a dozen different implementations of INextNumberGenerator? If not, don't make an interface.

Rather, it strikes me as the kind of thing that could be handled by a very short method:

static IEnumerable GenerateInfiniteSequence(
  T seed,
  Func next)
{
  T current = seed;
  while(true)
  {
    yield return current;
    current = next(current);
  }
}


There, done.

As another answer wisely pointed out: the mechanisms of OOP were designed for managing complexity when programming in the large, not for simple math. You just call Math.Cos. You don't create an instance of the Trigonometry class to get its ICosineProviderFactory.

C# lets you treat arbitrary sequences as mathematical objects; take advantage of that to write concise code.

Code Snippets

public static IEnumerable<T> UntilDuplicate<T>(
  this IEnumerable<T> items, IEqualityComparer<T> equals) 
{
  var seen = new HashSet<T>(equals);
  foreach(var item in items)
  {
    if (seen.Contains(item)) 
      yield break;
    seen.Add(item);
    yield return item;
  }
}
static IEnumerable<T> GenerateInfiniteSequence<T>(
  T seed,
  Func<T, T> next)
{
  T current = seed;
  while(true)
  {
    yield return current;
    current = next(current);
  }
}

Context

StackExchange Code Review Q#139519, answer score: 8

Revisions (0)

No revisions yet.