Best practice to refactor if staments in C# [closed] - c#

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
what's the best practice to refactor the following c# code: Can replace this with Strategy pattern?
private int GetAccountType(int balance)
{
if (balance <= 5000)
{
return 1;
}
else if (balance > 5000 && balance <= 10000)
{
return 2;
}
else if (balance >= 10001)
{
return 3;
}
_logger.LogError("Invalid AccountType");
// TO DO : Throw custom exception
throw new Exception("Invalid Balance");
}

As others have already mentioned Strategy would definitely be over-engineering in this case. Since your logging statement is never reached, I'd simply do:
private int GetAccountType(int balance)
{
return balance switch
{
<= 5000 => 1,
> 5000 and <= 10000 => 2,
>= 10001 => 3
};
}
But normally I would not refactor anything in this case - perhaps just remove the "else".

One easy simplification would be:
private int GetAccountType(int balance)
{
if (balance <= 5000)
{
return 1;
}
else if (balance <= 10000) // balance is certain to be > 5000
{
return 2;
}
else // balance is certain to be > 10000
{
return 3;
}
_logger.LogError("Invalid AccountType");
// TO DO : Throw custom exception
throw new Exception("Invalid Balance");
}
If you already checked that balance <= 5000 then in the else-if you don't need to check that balance > 5000 - you wouldn't get there otherwise.
For the rest your logic is clear this way - readability is also an important aspect!

In case you have many balances, I suggest extracting a model:
// Note, now you can add easily as many balances as you want
private static readonly IReadOnlyDictionary<int, Func<int>> s_Balances =
new Dictionary<int, int>() {
{ int.MinValue, 1}, // 1 starting from int.MinValue + 1
{ 5000, 2}, // 2 starting from 5001
{ 10000, 3}, // 3 starting from 10001
};
then you can easily query this model with a help of Linq:
using System.Linq;
...
private int GetAccountType(int balance) {
int key = s_Balances
.Keys
.Where(k < balance)
.Max();
return s_Balances[key];
}

Here you can refactor in another way :
int result = balance > 5000 && balance <= 10000 ? 2 : balance > 10000 ? 3 : 1;
Few lines
There are no fundamental difference between ternary and if/else.
Ternary is faster then if/else as long as no additional computation is required to convert the logic to us ternary. When it is a simply ternary operation, it has better readability as well.
If only statement is faster than if/else, so if the logic doesn’t require an else statement, do use it.
Performance Comparison : LINK
Strategy Design Pattern
It is behavioral design pattern and you can implement when you have such issues. This code doesn't require patterns as it will be over engineering.
Identification: Strategy pattern can be recognized by a method that lets nested object do the actual work, as well as the setter that allows replacing that object with a different one
Usage examples: It’s often used in various frameworks to provide users a way to change the behavior of a class without extending it.

Here is another option, but I think the if/else statement is more readable. I added it because it can be a useful technique when you need to look up values in an array for instance.
using static System.Math;
...
private int GetAccountType(int balance) => 1 + Min(Max(balance / 5000, 0), 2);

Related

Checking whether a number contains numbers 1 to n as factors

I am trying to solve Project Euler Challenge 5, What is the smallest possible number that is evenly divisible by all the numbers from 1 to 20.
My problem is that my isMultiple() method doesn't return true when it should.
* My Code *
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Challenge_5
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(isMultiple(2520, 10)); //should return True
Console.WriteLine(smallestMultiple(20)); //should return 232792560
Console.ReadLine();
}
static int factorial(int n)
{
int product = 1;
for (int i = 1; i <= n; i++)
{
product *= i;
}
return product; //returns the factorial of n, (n * n-1 * n-2... * 1)
}
static bool isMultiple(int number, int currentFactor)
{
bool returnBool = false;
if (currentFactor == 1)
{
returnBool = true; // if all factors below largestFactor can divide into the number, returns true
}
else
{
if (number % currentFactor == 0)
{
currentFactor--;
isMultiple(number, currentFactor);
}
}
return returnBool;
}
static int smallestMultiple(int largestFactor)
{
for (int i = largestFactor; i < factorial(largestFactor); i+= largestFactor) //goes through all values from the kargestFactor to largestFactor factorial
{
if (isMultiple(i, largestFactor))
{
return i; // if current number can be evenly divided by all factors, it gets returned
}
}
return factorial(largestFactor); // if no numbers get returned, the factorial is the smallest multiple
}
}
}
I know there are much easier ways to solve this, but I want the program to be used to check the lowest multiple of the numbers from 1 to any number, not just 20.
Help would be much appreciated.
EDIT
Thanks to help, i have fixed my code by changing line 42 from
isMultiple(number, currentFactor);
to
returnBool = isMultiple(number, currentFactor);
I also fixed the problem with not getting an accurate return value for smallestMultiple(20);
by changing some of the variables to long instead of int
Your problem is you forgot to use the output of isMultiple in your recursive part
if (number % currentFactor == 0)
{
currentFactor--;
returnBool = isMultiple(number, currentFactor); //you need a to save the value here.
}
Without assigning returnBool there is no way of knowing if the inner isMultiple returned true or not.
Scott's answer is perfectly valid. Forgive me if I'm wrong, but it sounds like you're a student, so in the interest of education, I thought I'd give you some pointers for cleaning up your code.
When writing recursive functions, it's (in my opinion) usually cleaner to return the recursive call directly if possible, as well as returning base cases directly, rather than storing a value that you return at the end. (It's not always possible, in complicated cases where you have to make a recursive call, modify the return value, and then make another recursive call, but these are uncommon.)
This practice:
makes base cases very obvious increasing readability and forces you to consider all of your base cases
prevents you from forgetting to assign the return value, as you did in your original code
prevents potential bugs where you might accidentally do some erroneous additional processing that alters the result before you return it
reduces the number of nested if statements, increasing readability by reducing preceding whitespace
makes code-flow much more obvious increasing readability and ability to debug
usually results in tail-recursion which is the best for performance, nearly as performant as iterative code
caveat: nowadays most compilers' optimizers will rejigger production code to produce tail-recursive functions, but getting into the habit of writing your code with tail-recursion in the first place is good practice, especially as interpreted scripting languages (e.g. JavaScript) are taking over the world, where code optimization is less possible by nature
The other change I'd make is to remove currentFactor--; and move the subtraction into the recursive call itself. It increases readability, reduces the chance of side effects, and, in the case where you don't use tail-recursion, prevents you from altering a value that you later expect to be unaltered. In general, if you can avoid altering values passed into a function (as opposed to a procedure/void), you should.
Also, in this particular case, making this change removes up to 3 assembly instructions and possibly an additional value on the stack depending on how the optimizer handles it. In long running loops with large depths, this can make a difference*.
static bool isMultiple(int number, int currentFactor)
{
if (currentFactor == 1)
{
// if all factors below largestFactor can divide into the number, return true
return true;
}
if (number % currentFactor != 0)
{
return false;
}
return isMultiple(number, currentFactor - 1);
}
* A personal anecdote regarding deep recursive calls and performance...
A while back, I was writing a program in C++ to enumerate the best moves for all possible Connect-4 games. The maximum depth of the recursive search function was 42 and each depth had up to 7 recursive calls. Initial versions of the code had an estimated running time of 2 million years, and that was using parallelism. Those 3 additional instructions can make a HUGE difference, both for sheer number of additional instructions and the amount L1 and L2 cache misses.
This algorithm came up to my mind right now, so please anyone correct me if i'm wrong.
As composite numbers are made by multiplication of prime numbers (and prime numbers can not be generated from multiplication of any other numbers) so the number must be multiplied to all prime numbers, some numbers like 6 when they are reached our smallest number is dividable (as its already multiplied to 2 and 3), but for those that are not, multiplying by a prime number (that is obviously less than the number itself so should be in our prime list) would make our smallest dividable to that number too. so for example when we get to 4, multiplying by2` (a prime number less than 4) would be enough, 8 and 9 the same way, ...
bool IsPrime(int x)
{
if(x == 1) return false;
for(int i=2;i<=Math.Sqrt(x);i+=2)
if(x%i==0) return false;
return true;
}
int smallest = 1;
List<int> primes = new List<int>();
for(int i=1;i<=20;i++)
if(IsPrime(i))
{
smallest *= i;
primes.Add(i);
}
else if(smallest % i != 0)
for(int j=0;j<primes.Count;j++)
if((primes[j]*smallest)%i == 0)
{
smallest *= primes[j];
break;
}
Edit:
As we have list of prime numbers so the best way to find out if a number is prime or not would be:
bool IsPrime(int x)
{
if(x == 1) return false;
for(int i = 0; i< primes.Count; i++)
if(x%primes[i] == 0) return false;
return true;
}

MinHeap implementation in c# [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am working on a MinHeap implementation for school and I have encountered a problem. The code typically works well but sometimes generates an argument out of range exception in my heapify method. I have tried to isolate the problem but I am a terrible debugger.
Here is my code for the function:
private void Heapify(int i)
{
int least;
int leftchild = 2 * (i + 1) - 1;
int rightchild = 2 * (i + 1);
if (leftchild < heap.Count && (heap[rightchild].CompareTo(heap[i]) < 0))
{
least = 1;
}
else
{
least = i;
}
if (rightchild < heap.Count && (heap[rightchild].CompareTo(heap[least]) < 0))
{
least = rightchild;
}
if (least != i)
{
T temp = heap[i];
heap[i] = heap[least];
heap[least] = temp;
this.Heapify(least);
}
Out of range exceptions are generally easy to track down. Basically, you have to make sure that whenever you're accessing an item in an array via indexes, said array's count / length is greater than the index. In other words, ensure that in every call to heap[#index], #index < heap.Count (either by straight checking it or by your method's logic)
if (leftchild < heap.Count && (heap[rightchild].CompareTo(heap[i]) < 0))
If rightchild >= heap.Count, this will give you an exception.

Reducing runtime of method from 24 hours to 1 hour [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have a 'For' loop I need to run from 0 to 2,147,483,647 (int.MaxValue). Inside the loop I'm calling a method that calculates a very complex equation and it returns the value for each parameter. I wish to get the highest return value.
The total runtime of my for loop takes about full 24 hours. How and can I reduce the runtime of my code to only one hour? Maybe 4 hours if I will use distributed computing?
static void Main(string[] args)
{
CheckValuesZeroToMaxInt();
Console.ReadLine();
}
private static void CheckValuesZeroToMaxInt()
{
for (int i = 0; i < int.MaxValue; i++)
{
ComplexMath(i);
}
}
I know how to use 'Parallel.For', but still this offers very little help. How can I take my c# code and run it on a cluster of computers to get the return value from my method in no time? Is there a site that offers this service for a low or free cost?
The easiest way to do it on one machine would be with the Parallel.For-loop
private static void CheckValuesZeroToMaxInt()
{
object maxValueLock = new object();
int maxValue = int.MinValue;
Parallel.For(0, int.MaxValue, i =>
{
int tmpMaxValue = ComplexMath(i);
if (tmpMaxValue > maxValue)
{
lock(maxValueLock)
{
if (tmpMaxValue > maxValue)
maxValue = tmpMaxValue;
}
}
}
Console.WriteLine(maxValue);
}
However, since your ComplexMath function only takes ~0.04ms ((24 / (2^31)) * 3600 * 1000) to execute, you'd maybe get stuck with the constant locking and wouldn't get almost any improvement. To prevent this from happening, we can change the above function to execute more steps within a thread.
const long million = 1000*1000;
private static void CheckValuesZeroToMaxInt()
{
object maxValueLock = new object();
int maxValue = int.MinValue;
Parallel.For(0, (int.MaxValue / million) + 1, i =>
{
int privateMaxValue = int.MinValue;
for (long j = i * million; j < (i+1) * million && j < int.MaxValue; j++)
{
int tmpMaxValue = ComplexMath(j);
if (tmpMaxValue > privateMaxValue)
{
privateMaxValue = tmpMaxValue;
}
}
lock(maxValueLock)
{
if (privateMaxValue > maxValue)
maxValue = privateMaxValue;
}
}
Console.WriteLine(maxValue);
}
The second version should scale pretty much linearly with the number of processors, so if you run it on a machine with 24 cores, it will finish within 1h. Of course instead of Parallel.For you can use any kind of communication to distribute the computation between multiple machines.

Knuth's algorithm for solving MasterMind with 5 guesses [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I read knuth's algorithm in wikipedia and I wonder about the 3rd step. If I understand correct for each option (even if not removed in step 2), we calculate how many possible guesses would have removed for every ffedback. for that step we take minimum. after, we find the maximum on the minimums and take the code the max belong to it. that will be our second guess. Im not sure what can be done next because,each time we dont change the max on the minimums (or what is the minimum). In addition, how can I implement this step in c# for example. How can I procceed and how can I implement that step? What does the minimum on the feedbacks really mean?
You need a constant collection of all possible outcomes, a collection of the remaining alternatives, and a method that can compute the outcome given a guess and a solution.
First, model the relevant domain objects:
public class Outcome
{
public int White { get; set; }
public int Black { get; set; }
}
public class Combination
{
// however you like to model this
}
Then, create the method that checks the guess against the secret:
public static Outcome Check(Combination guess, Combination solution)
{
// your implementation
}
Now the algorithm is as follows:
Outcome[] outcomes = new[] { new Outcome { White = 0, Black = 0 },
new Outcome { White = 1, Black = 0 },
// ... all other possibilities
};
// assume we have some list of combinations
int min = Integer.MaxValue;
Combination minCombination = null;
foreach (var guess in combinations)
{
int max = 0;
foreach (var outcome in outcomes)
{
var count = 0;
foreach (var solution in combinations)
{
if (Check(guess, solution) == outcome)
count++;
}
if (count > max)
max = count;
}
if (max < min)
{
min = max;
minCombination = guess;
}
}
At the end of the loop, minCombination is your next guess.
EDIT I messed up min and max in the first version, that is fixed now. The inner count gives the number of remaining options, provided the chosen combination and the assumed outcome. We want the maximum over the outcomes (the worst possible result for the chosen combination is the one that leaves most options remaining). After that we want the minimum over the combinations (the best possible combination is the one that leaves least options remaining in the worst case).
If you like Linq, you could also write the algorithm as
combinations.MaxBy(guess =>
outcomes.Min(outcome =>
combinations.Count(solution =>
Check(guess, solution) == outcome)));
in which I used MaxBy from the MoreLinq project.

Factorial function - design and test

I'm trying to nail down some interview questions, so I stared with a simple one.
Design the factorial function.
This function is a leaf (no dependencies - easly testable), so I made it static inside the helper class.
public static class MathHelper
{
public static int Factorial(int n)
{
Debug.Assert(n >= 0);
if (n < 0)
{
throw new ArgumentException("n cannot be lower that 0");
}
Debug.Assert(n <= 12);
if (n > 12)
{
throw new OverflowException("Overflow occurs above 12 factorial");
}
int factorialOfN = 1;
for (int i = 1; i <= n; ++i)
{
//checked
//{
factorialOfN *= i;
//}
}
return factorialOfN;
}
}
Testing:
[TestMethod]
[ExpectedException(typeof(OverflowException))]
public void Overflow()
{
int temp = FactorialHelper.MathHelper.Factorial(40);
}
[TestMethod]
public void ZeroTest()
{
int factorialOfZero = FactorialHelper.MathHelper.Factorial(0);
Assert.AreEqual(1, factorialOfZero);
}
[TestMethod]
public void FactorialOf5()
{
int factOf5 = FactorialHelper.MathHelper.Factorial(5);
Assert.AreEqual(5*4*3*2*1,factOf5);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void NegativeTest()
{
int factOfMinus5 = FactorialHelper.MathHelper.Factorial(-5);
}
I have a few questions:
Is it correct? (I hope so ;) )
Does it throw right exceptions?
Should I use checked context or this trick ( n > 12 ) is ok?
Is it better to use uint istead of checking for negative values?
Future improving: Overload for long, decimal, BigInteger or maybe generic method?
Thank you
It looks right to me, but it would be inefficient with larger numbers. If you're allowing for big integers, the number will keep growing with each multiply, so you would see a tremendous (asymptotically better) increase in speed if you multiplied them hierarchically. For example:
bigint myFactorial(uint first, uint last)
{
if (first == last) return first;
uint mid = first + (last - first)/2;
return myFactorial(first,mid) * myFactorial(1+mid,last);
}
bigint factorial(uint n)
{
return myFactorial(2,n);
}
If you really want a fast factorial method, you also might consider something like this:
Factor the factorial with a modified Sieve of Eratosthenes
Compute the powers of each prime factor using a fast exponentiation algorithm (and fast multiplication and square algorithms)
Multiply all the powers of primes together hierarchically
Yes, it looks right
The exceptions seem OK to me, and also as an interviewer, I can't see myself being concerned there
Checked. Also, in an interview, you'd never know that 12 just happened to be the right number.
Uint. If you can enforce something with a signature instead of an exception, do it.
You should just make it long (or bigint) and be done with it (int is a silly choice of return types here)
Here are some follow-up questions I'd ask if I were your interviewer:
Why didn't you solve this recursively? Factorial is a naturally recursive problem.
Can you add memoization to this so that it does a faster job computing 12! if it's already done 11!?
Do you need the n==0 case here?
As an interviewer, I'd definitely have some curveballs like that to throw at you. In general, I like the approach of practicing with a whiteboard and a mock interviewer, because so much of it is being nimble and thinking on your feet.
In the for cycle you can start with for (int i = 2...). Multiplying by 1 is quite useless.
I would have throw a single ArgumentOutOfRangeException for both < 0 and > 12. The Debug.Assert will mask the exception when you are using your unit test (you would have to test it in Release mode).

Categories