C# return does not exit the function - c#

So, I'm programming a function that asks a random question from the dictionary until all the questions have been asked and I ran into an issue. When the list of randomly generated numbers already contains the new generated number, the program calls the Ask function, as it should. But then, when it gets to the final return line, it calls the function Ask again, instead of returning the list of numbers.
static List<int> Ask(Dictionary<string,string> Questions, List<int> random, int count)
{
bool contains = false;
var rando = new Random();
int num = Convert.ToInt32(rando.Next(0, count));
if (random.Count >= count)
{
Console.WriteLine("No more questions! Quitting");
System.Environment.Exit(1);
}
foreach (int number in random)
{
if (number == num)
{
contains = true;
}
}
if (contains == true)
{
Ask(Questions, random, count);
}
random.Add(num);
var randomEntry = Questions.ElementAt(num);
String randomKey = randomEntry.Key;
String randomValue = randomEntry.Value;
Console.WriteLine(randomKey);
if (Console.ReadLine() == randomValue)
{
Console.WriteLine("Correct!");
}
else
{
Console.WriteLine("Wrong!");
}
return random;
}

I think the problem is in your recursive call to the function Ask. When you exit from the recursive call you are in the previous call still inside the Ask method and there is no way to avoid the remainder of the code following the Ask call.
I don't think yor really need a recursive method for this. Just check if the random number generated is already in your list of asked questions and repeat the random generation until you find a question not asked before....
Here the refactored code with variable names less confusing
List<int> Ask(Dictionary<string,string> Questions, int count)
{
List<int> askedList = new List<int>();
var rnd = new Random();
int questionIndex = rnd.Next(0, count);
while(askedList.Count < count)
{
if(!askedList.Any(number => number == questionIndex))
{
askedList.Add(questionIndex);
var questionEntry = Questions.ElementAt(questionIndex);
string questionText = questionEntry.Key;
string questionAnswer = questionEntry.Value;
Console.WriteLine(questionText);
if (Console.ReadLine() == questionAnswer)
{
Console.WriteLine("Correct!");
}
else
{
Console.WriteLine("Wrong!");
}
}
questionIndex = rnd.Next(0, count);
}
return askedList;
}

Your code is a little bit messy. But random var isn't updated after recursively calling the function. Try this:
random = Ask(Questions, random, count);

Related

C# - HackerRank simpleArraySum

Now, I may get negative points because perhaps somewhere in vast internet there is already an answer to this but I tried to look for it and I simply couldnt find it.
The gist of the problem is that HackerRanks wants you to create an array with a size decided by the user, then have the user add its values (integers) and finally have the program sum its values.
There are plenty of ways to do it and I already know how to but my problem is that I just can't understand Hackerrank's code sample in C# it gave me. I commented the parts I don't understand, which is most of it:
static int simpleArraySum(int n, int[] ar) {
// Complete this function
int sum = 0;
foreach( var item in ar){
sum += item;
}
return sum;
}
static void Main(String[] args) {
//I know what this does
int n = Convert.ToInt32(Console.ReadLine());
//I am lost here, just why create a string array and add the split method?
string[] ar_temp = Console.ReadLine().Split(' ');
//I dont understand here neither, what is it converting? What is the parse for?
int[] ar = Array.ConvertAll(ar_temp,Int32.Parse);
//Why send the n when all you need is the array itself?
int result = simpleArraySum(n, ar);
Console.WriteLine(result);
}
I know some people hate HackerRank, and honestly, I do too but it does gives me some nice ways to test my limited skills in coding with c# and testing my logic. So, if there are better sites that helps you test your logic as a CS please share them with me.
Here is the code I made to solve this problem in Visual Studio but for some stupid reason Hackerrank wont accept it unless I make custom inputs:
//This code can be potentially shorter using the code commented further below.
//For practice's sake, it was made longer.
static int simpleArraySum(int[] arr_temp)
{
int total = 0;
foreach (var item in arr_temp)
{
total += item;
}
return total;
}
static void Main(String[] args)
{
int n = Convert.ToInt32(Console.ReadLine());
int[] arr_temp = new int[n];
for (int i = 0; i < n; i++)
{
arr_temp[i] = Convert.ToInt32(Console.ReadLine());
}
int result = simpleArraySum(arr_temp);
//int result = arr_temp.Sum();
Console.WriteLine(result);
Console.ReadLine();
}
You need to convert to string array since if you're on the main method, all it gets are string values from the argument list. To get the sum then you need to convert the string into a usable number / integer.
I agree that it doesn't make sense to send the first argument n in simpleArraySum because n is simply unused.
as for the part int[] ar = Array.ConvertAll(ar_temp,Int32.Parse); it simply tries to take in all the integers into the array. It is also risky because if you accidentally pass in a string then it will throw an error i.e. pass in "3 4 1 f" <- f will throw an exception, unless this is the desired behaviour.
Personally I think the main method should not be interested in getting involved too much with the data, the heavy lifting should be done in the methods. The better version perhaps would be to modify simpleArraySum and refactor that line in like:
static int simpleArraySum(string input)
{
String[] fields = input.Split(null);
List<int> vals = new List<int>();
foreach (string i in fields)
{
var j = 0;
if (Int32.TryParse(i, out j)) vals.Add(j);
}
int sum = 0;
foreach (var item in vals)
{
sum += item;
}
return sum;
}
I introduced the use of generic list because it's more readable if not cleaner, although the use of List might look overkill to some programmers and might not be as light weight as just using an array, hence on the other hand you can easily stick to using arrays except that it needs to be initialized with the length i.e. int[] vals = new int[fields.Length]; Roughly:
static int simpleArraySum(string input)
{
String[] fields = input.Split(null);
int[] vals = new int[fields.Length];
for (int i = 0; i < fields.Length; i++)
{
var j = 0;
if (Int32.TryParse(fields[i], out j)) vals[i] = j;
}
int sum = 0;
foreach (var item in vals)
{
sum += item;
}
return sum;
}
here my code i hope that helps
static int simpleArraySum(int[] ar,int count) {
if (count > 0 && count <= 10000)
{
if (count == ar.Length)
{
if (!ar.Any(item => (item < 0 || item >= 10000)))
{
return ar.Sum();
}
}
}
return 0;
}
and in main
int arCount = Convert.ToInt32(Console.ReadLine());
int[] arr = Array.ConvertAll(Console.ReadLine().Split(' '), arTemp => Convert.ToInt32(arTemp));
int result = simpleArraySum(arr, arCount);
Console.WriteLine(result);
since Array.ConvertAll() takes a string and convert it to one type array
int or float for example
For users still looking for a 100% C# solution: In above mentioned coding websites do not modify the main function. The aim of the test is to complete the function via the online complier.
using System.Linq;
public static int simpleArraySum(List<int> ar)
{
int sum = ar.Sum();
return sum;
}

ASP.NET MVC app: Debugging a function that returns "true" if input number is a Happy number using Lists vs Hashsets in array

So I'm working on an MVC view model that will tell a user if a given integer input is a Happy Number or not. As stated, the final code will sit in an MVC view model, but in the meantime, I've been checking the logic by debugging the code as a separate console app. I've got the console app working, but when I migrate the code back to the view model and then debug in the full application, literally nothing happens and IIS eventually times out the request. My view and controller implementations look solid (I'm leaving out that code for now), so I think I'm screwing up the logic in my actual model. Thoughts?
The code explained in further detail:
I'm new to OO programming, so for the sake of my own comprehension, I have split up the program into to two separate functions (Yes, I am aware this is bad practice, but it's homework). The first function (see below), which I've called "sumPowered", takes an integer input, separates the individual digits into an array, squares the individual digits, and returns the sum of the squared digits. Next, I have created a validation function called "isHappy" in which I call the preceding sumPowered function, pass through the parameters, generate a result array, and compare them to the happy number conditions.
To provide a clear outline, the first block of code below is the working code as it appears in my console app. The second block demonstrates how the code appears in the view model (doesn't work).
EDIT: I should have clarified that I am not producing any build errors or errors in the console view of Chrome's property inspector when I run the MVC code, yet the function did not work. I have since found a fix (answer post pending).
CONSOLE APP
//CONSOLE APP
namespace MoreCSharpPractice
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter a number");
int number = Convert.ToInt32(Console.ReadLine());
isHappy(number);
}
public static int sumPowered(int num, int pow)
{
int sum = 0;
List<int> numsL = new List<int>();
while (num > 0)
{
numsL.Add(num % 10);
num = num / 10;
}
int[] nums = numsL.ToArray();
for (int a = 0; a < nums.Length; a++)
{
sum += Convert.ToInt32(Math.Pow(Convert.ToDouble(nums[a]), Convert.ToDouble(pow)));
}
return sum;
}
//HAPPY NUMBER
//return true if 'number' is a happy number.
private static void isHappy(int number)
{
List<int> sumArray = new List<int>();
bool running = true;
while (running)
{
int result = sumPowered(number, 2);
if (result == 1)
{
running = false;
Console.WriteLine("Is a Happy Number!");
}
else if (sumArray.Contains(result))
{
running = false;
//return false;
Console.WriteLine("Is not a Happy Number");
}
number = result;
sumArray.Add(result);
}
//return true;
Console.ReadKey();
}
}
}
MVC CODE
//VIEW MODEL CODE: SUM POWERED FUNCTION
public static int sumPowered(int num, int pow)
{
int sum = 0;
List<int> numsL = new List<int>();
while (num > 0)
{
numsL.Add(num % 10);
num = num / 10;
}
int[] nums = numsL.ToArray();
for (int i = 0; i < nums.Length; i++)
{
sum += Convert.ToInt32(Math.Pow(Convert.ToDouble(nums[i]), Convert.ToDouble(pow)));
}
return sum;
}
//VIEW MODEL CODE: HAPPY NUMBER FUNCTION
//return true if 'number' is a happy number.
private static bool isHappy(int number)
{
List<int> sumArray = new List<int>();
while (true)
{
int result = sumPowered(number, 2);
if (result == 1)
{
return true;
}
else if (sumArray.Contains(result))
{
return false;
}
number = result;
sumArray.Add(result);
}
}
Determined that this wasn't a logic error but instead a performance issue while running debug. By changing the data type of variable sumArray in my isHappy() function from a List to a HashSet, I was able to run through the function without having to iterate through an entire list when invoking else if (sumArray.Contains(result)).
//HAPPY NUMBER
//return true if 'number' is a happy number.
private static bool isHappy(int number)
{
HashSet<int> sumArray = new HashSet<int>();
while (true)
{
int result = sumPowered(number, 2);
if (result == 1)
{
return true;
}
else if (sumArray.Contains(result))
{
return false;
}
number = result;
sumArray.Add(result);
}
}

C# - Returning a Unique Random Number [duplicate]

This question already has answers here:
Random number generator only generating one random number
(15 answers)
Closed 10 years ago.
I have searched this for hours and I'm not getting it. I don't seem to know how to return values using Fisher-Yates and many ways listed. I'm dying here.
I can get a RandomNumber, but this is reused over and over. I need it to be unique everytime when returned (or so I tend to think is possible).
I need help understanding what I should do, why each part does, and stuff for dummies. This is what works:
private int RandomNumber(int min, int max)
{
Random random = new Random();
return random.Next(min, max);
}
And this is what I'm putting it into and it working (but not unique random numbers are used)... I only included what I felt needed to be looked at and where it is positioned:
private void ComputersTurn()
{
Control.ControlCollection coll = this.Controls;
foreach (Control c in coll)
{
if (...)
{
if (...)
{
if (...)
{
if ((c.Name == "btn" + Convert.ToString(RandomNumber(1,9)) && (c.Enabled != false) ))
{
if (...)
{
//code here
}
}
}
}
}
}
}
Again, RandomNumber works...but it's not unique. I wish to learn how to return a unique number (if possible).
Are you simply trying to return all the integers from min to max with their order permuted? This is the only way it makes sense to me to want a sequence of random integers in a given range such that each random is guaranteed unique...
Assuming I'm correct, you should be able to easily find code for random permutation of an array.
The only way to generate unique numbers by Random is to define it in your class like this:
public static class RandomGenerator
{
private static readonly Random _random = new Random();
public static int GenRand(int x, int y)
{
return _random.Next(x, y);
}
}
In your case try to use this code this way:
private void ComputersTurn()
{
Control.ControlCollection coll = this.Controls;
foreach (Control c in coll)
{
if (...)
{
if (...)
{
if (...)
{
if ((c.Name == "btn" + Convert.ToString(RandomGenerator.GenRand(1, 9)) && (c.Enabled != false) ))
{
if (...)
{
// code here
}
}
}
}
}
}
}
Instantiate the Random class only once.
private Random random = new Random();
private int RandomNumber(int min, int max)
{
var result = this.random.Next(min, max);
return result;
}
try to put declaration of your instance of Random class outside the function so that the you can get every time different number if you want to make sure that random numbers are not going to be duplicated you can use and List to store every generated number
class Classname
{
private Random random = new Random();
//your code
}

only return random number when it is unique

My brain is melting today and i cannot think how to do this simple bit of code. numberList is a string of numbers seperated by commas like '2, 34, 10' etc.. when i request a random number i need to check if the string has the number, if it does i want to keep requesting a random number until the random number is definitely not in the string. i cant think what kind of loop i would do to get this to work:
Random r = new Random();
public int RandomPos(int max) {
int i;
do {
i = r.Next(max) + 1;
}
while (!numberList.Contains(i.ToString()));
return i;
}
I'll just explain in text instead of code because I'm too lazy to write the code right now:
Use String.Split to break your list into an array, then (if you need to) parse it into integers.
Use Enumerable.Range(0, max).ToArray() to create a list of all the numbers you could select.
Subtract the first list from the second.
Randomly select an element from the final list.
This has the benefit that you don't need to keep picking things randomly and retrying in a potentially-infinite-but-not-really-in-practice loop.
edit: here's some code
string[] invalid = numberList.Split(", ");
var list = Enumerable.Range(0, max).Where(x => !invalid.Contains(x.ToString())).ToArray();
return list[r.Next(list.Count)];
Remove the !
do
{
i = r.Next(max) + 1;
}
while (numberList.Contains(i.ToString()));
Try it with this:
static string invalidNumbers = "0,1,2,3,4,5";
static Random random = new Random();
static int Randomize()
{
var randomInt = random.Next(0, 10);
if (!invalidNumbers.Split(',').Contains(randomInt.ToString()))
{
return randomInt;
}
else
{
return Randomize();
}
}
Providing a simple answer, you don't need Split(). This assumes no spaces between numbers, modify accordingly:
String modifiedNumberList = "," + numberList + ",";
do {
i = r.Next(max) + 1;
}
while (modifiedNumberList.Contains("," + i.ToString() + ","));
edit: I believe BrokenGlass is also right, you shouldn't have the "!", removed from my solution.
Maybe this is what you want? I used a regular while instead since I think they are easier to read, and the only thing I think you get wrong was the !.
public int RandomPos(int max) {
int i = r.Next(max);
var intList = numberList.Split(',').ToDictionary<string,int>((n) => int.Parse(n));
while(intList.Contains(i))
{
i = r.Next(max);
}
return i;
}
Assuming I need to split the numberList first to if they are in a string. That would make the third row look like:
A modification of #Dave's reply:
static string invalidNumbers = "0,1,2,3,4,5";
static Random random = new Random();
static int Randomize()
{
var randomInt = random.Next(0, 10);
var splitString = invalidNumbers.Split(',');
while (splitString.Contains(randomInt.ToString()))
{
randomInt = random.Next(0, 10);
}
return randomInt;
}
A couple ways to improve this:
1) Use a List<int> or something instead of a string to make your life easier
2) if max is small (say <1000 or something) generate the list of all possible values, order them randomly, and return numbers in sequence from that list.
As the number of "used" numbers approaches "max" you could end up in a very long loop before you get an unused number. For values of max over a couple hundred, this could actually be of consequence. This may or may not be a problem in your situation.
This code will cover all cases:
"1,2,3,4,5"...
"1, 2, 3,4,5"...
private static int GetRandomNumber(string existingNumbers, int max)
{
string[] existingNumbersArray = existingNumbers.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<string> existingNumbersList = new List<string>();
foreach (string number in existingNumbersArray)
{
existingNumbersList.Add(number.Trim());
}
while (true)
{
Random rnd = new Random();
int value = rnd.Next(max);
if (!existingNumbersList.Contains(value.ToString()))
{
return value;
}
}
}
You can even take out this part:
string[] existingNumbersArray = existingNumbers.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
List<string> existingNumbersList = new List<string>();
foreach (string number in existingNumbersArray)
{
existingNumbersList.Add(number.Trim());
}
so it will not be called each time you call GetRandomNumber function.
My addition to all the other answers..
const string invalidNumbers = "0,1,2,3,4,5";
Random random = new Random();
int value = 0;
List<int> tmpList = new List<int>();
foreach (var x in invalidNumbers.Split(','))
{
tmpList.Add(Int32.Parse(x));
}
do
{
value = random.Next(0, 10);
}
while (tmpList.Contains(value));
return value
Edit: missunderstood the question for the first post, anyway, here is a recursive solution.
It seems better to keep the numbers in a List, but if it is required to follow the format you asked, here it is:
const int MAX_ATTEMPTS = 10;
Random r = new Random();
string nlist = "2, 34, 10";
public int RandomPos(int max_val)
{
List<string> used = nlist.Replace(" ","").Split(',').ToList();
return _RandomPos(MAX_ATTEMPTS, max_val, used);
}
private int _RandomPos(int tl, int max, List<string> used)
{
if (tl <= 0)
throw new Exception("Could not generate random number. Too many tries.");
else
{
int rnum = r.Next(max);
if (!used.Contains(rnum.ToString()))
{
nlist += ", " + rnum.ToString();
return rnum;
}
else
return _RandomPos(tl - 1, max, used);
}
}
I realize there are a lot of entries, but I don't see any with some decent error checking. That being said, this will offer a few things:
Won't waste any effort when there's nothing to disqualify
Will only select from a range of possible choices
Will flag -1 if a number can't be chosen within the max range and not in the disqualifying list
So here goes:
public int RandomPos(int max)
{
// compile the list of numbers we need to disqualify
List<int> disqualified = numberList.Split(new[]{',',' '},StringSplitOptions.RemoveEmptyEntries).Select(n => int.Parse(n)).ToList();
// Nothing to check against, save the CPU cycles
if (disqualified.Count == 0)
return (new Random(DateTime.Now.Millisecond)).Next(max) + 1;
// make a list of everything that's possible for a choice
List<int> valid = Enumerable.Range(0, max).Where(r => !disqualified.Contains(r)).ToList();
// return either a valid result, or -1 if there are no valid results
return (valid.Count == 0 ? -1 : valid[(new Random(DateTime.Now.Millisecond)).Next() % valid.Count]);
}
.Split will do the work, the following code will work as well, just for fun (replace the line while (!numberList.Contains(i.ToString())); in your code instead of checking i.ToString() check ","+i.ToString()+"," PLUS the beginning and ending. You need to adjust it if you have a space after ","):
while (!numberList.StartsWith(i.ToString()+",")&&
!numberList.Contains(","+i.ToString()+",")&&
!numberList.EndsWith(","+i.ToString()));
// if numberList is large then use HashSet<int> rather than a plain int[] array
int[] nums = numberList.Split(new[] { ',', ' ' },
StringSplitOptions.RemoveEmptyEntries)
.Select(int.Parse)
.ToArray();
int i;
while (nums.Contains(i = r.Next(max) + 1));
return i;
(You should also add a check to ensure that you don't end up in an infinite loop if/when numberList contains all the possible values that might be produced by the rng.)

How to generate random values that don't keep giving me the same value in 'runs' of numbers?

Hi I coded this OneAtRandom() extension method:
public static class GenericIListExtensions
{
public static T OneAtRandom<T>(this IList<T> list)
{
list.ThrowIfNull("list");
if (list.Count == 0)
throw new ArgumentException("OneAtRandom() cannot be called on 'list' with 0 elements");
int randomIdx = new Random().Next(list.Count);
return list[randomIdx];
}
}
Testing it using this unit test fails:
[Test]
public void ShouldNotAlwaysReturnTheSameValueIfOneAtRandomCalledOnListOfLengthTwo()
{
int SomeStatisticallyUnlikelyNumberOf = 100;
IList<string> list = new List<string>() { FirstString, SecondString };
bool firstStringFound = false;
bool secondStringFound = false;
SomeStatisticallyUnlikelyNumberOf.Times(() =>
{
string theString = list.OneAtRandom();
if (theString == FirstString) firstStringFound = true;
if (theString == SecondString) secondStringFound = true;
});
Assert.That(firstStringFound && secondStringFound);
}
It seems that int randomIdx = new Random().Next(list.Count);is generating the same number 100 times in a row, I think possibly because the seed is based on the time?
How can I get this to work properly?
Thanks :)
You shouldn't be calling new Random()for every iteration because it causes it to be reseeded and generate the same sequence of numbers again. Create one Random object at the start of your application and pass it into your function as a parameter.
public static class GenericIListExtensions
{
public static T OneAtRandom<T>(this IList<T> list, Random random)
{
list.ThrowIfNull("list");
if (list.Count == 0)
throw new ArgumentException("OneAtRandom() cannot be called on 'list' with 0 elements");
int randomIdx = random.Next(list.Count);
return list[randomIdx];
}
}
This also has the advantage of making your code more testable as you can pass in a Random that is seeded to a value of your choice so that your tests are repeatable.
No; it's generating the same number 100 times because you're not seeding the generator.
Move the "new Random()" to the constructor or a static var, and use the generated object.
You could use a seed based on the current time to create the instance of Random. A sample on MSDN uses the following code:
int randomInstancesToCreate = 4;
Random[] randomEngines = new Random[randomInstancesToCreate];
for (int ctr = 0; ctr < randomInstancesToCreate; ctr++)
{
randomEngines[ctr] = new Random(unchecked((int) (DateTime.Now.Ticks >> ctr)));
}

Categories

Resources