How can I prevent StackOverflowException in my code - c#

In my code, I cannot figure out why I keep getting 'Process is terminating due to StackOverflowException.' only on the second output.
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
namespace _2018JuniorQ5
{
class Program
{
//Variable Decleration
public static int pages = 0;
public static string[] bookFormat;
public static List<string> alreadyChecked = new List<string>();
public static List<string> nodesToCheck = new List<string>();
public static int level = 0;
public static List<string> childrenNodes = new List<string>();
public static void Main(string[] args)
{
//Get input
pages = Convert.ToInt32(Console.ReadLine());
bookFormat = new string[pages];
for (int x=0; x<pages; x++)
{
bookFormat[x] = Console.ReadLine();
}
//Display if all pages are reachable
Console.WriteLine(getReachablePages(1));
//Find shortest path
List<string> NodeBegin = new List<string>();
NodeBegin.Add("1");
Console.WriteLine(getShortestPath(NodeBegin));
}
public static string getReachablePages(int pageToCheck)
{
string[] options=(bookFormat[pageToCheck - 1]).Split(' ');
alreadyChecked.Add(Convert.ToString(pageToCheck));
for (int a=1; a<=Convert.ToInt32(options[0]); a++)
{
if (!alreadyChecked.Contains(options[a]))
{
getReachablePages(Convert.ToInt32(options[a]));
}
}
if (alreadyChecked.Count == pages)
{
return "Y";
}
else
{
return "N";
}
alreadyChecked.Clear();
}
public static int getShortestPath(List<string> nodesToCheck)
{
level++;
childrenNodes.Clear();
for (int q = 0; q < nodesToCheck.Count; q++)
{
string[] options = bookFormat[Convert.ToInt32(nodesToCheck[q])-1].Split(' ');
if (options[0] == "0")
{
return level;
}
else
{
for (int t = 1; t < options.Length; t++)
{
if (!alreadyChecked.Contains(options[t]))
{
childrenNodes.Add(options[t]);
alreadyChecked.Add(nodesToCheck[q]);
}
}
}
nodesToCheck.Clear();
}
return getShortestPath(childrenNodes);
}
}
}
The first output from the getReachablePages method works, and does not give any errors. However, the second output from the getShortestPath gives the "Process is terminating due to StackOverflowException" error. Can someone explain why the getReachablePages method works, but the getShortestPath method doesn't work?

The problem at the moment is that List<string> is a reference type, so when you pass childrenNodes to getShortestPath (getShortestPath(childrenNodes)), you're actually passing a reference to the same list in memory.
The next thing you do is call childrenNodes.Clear(), which empties that list. Because nodesToCheck and childrenNodes are both pointing at the same list, calling childrenNodes.Clear() means that you have no nodes to check.
Why does this cause a StackOverflowException? It causes one because you have no exit condition for when nodesToCheck is empty. You just keep calling the same method over and over.
I propose the following solution:
public static int getShortestPath(List<string> nodesToCheck)
{
if (!nodesToCheck.Any())
{
return -1;
}
var childrenNodes = new List<string>();
level++;
for (int q = 0; q < nodesToCheck.Count; q++)
{
string[] options = bookFormat[Convert.ToInt32(nodesToCheck[q])-1].Split(' ');
if (options[0] == "0")
{
return level;
}
else
{
for (int t = 1; t < options.Length; t++)
{
if (!alreadyChecked.Contains(options[t]))
{
childrenNodes.Add(options[t]);
alreadyChecked.Add(nodesToCheck[q]);
}
}
}
nodesToCheck.Clear();
}
return getShortestPath(childrenNodes);
}
When nodesToCheck is empty, return -1 (i.e. no path).
Create a new List<string> for childrenNodes within the getShortestPath method.
While this should fix your problem, I would recommend making your entire method self-contained. It's essentially a stateless method, but you're maintaining state outside the method, which led to the problem you have seen, and could lead to more problems if you call this method in a multi-threaded environment.
The other odd thing I noticed, is that you're looping through nodesToCheck, but the way your code is written means that you will only ever consider the first node because you clear nodesToCheck at the end of the first iteration, leaving the list empty. Further more, you're adding nodesToCheck[q] to alreadChecked once per item in options, which I'm sure can't be right.
I recommend learning to use the debugger in Visual Studio. The debugger allows you to step through your code line-by-line, inspect variables, etc. - this will help you locate problems in your code.
P.S. While it is not the correct solution to your problem, if you wish to copy a list you can use LINQ's ToList() method: var listB = listA.ToList();

Related

How do i acces individual Values in a hash table

I have an assignment where i have to take an string input and apply zipfs law to it. Im having issues accessing the value in a hashtable. Whenever i find a word that already exist i'm supposed to update the wordcounter +1. What ends up happening is the counter applies to all the values in the hash table, and i get ridiculously high numbers. I cant wrap my head around how i'm supposed to give an individual counter for each keyvalue
Here is the code:
using System;
using Spire.Doc;
using System.Collections;
using System.Collections.Generic;
namespace Zipf
{
class Program
{
public static Hashtable wordRegister;
static void Main(string[] args)
{
String[] ArrLoop = TextSplitter();
int[] wordCount = new int[ArrLoop.Length];
int count = 1;
wordRegister = new Hashtable();
for(int i = 0; i < ArrLoop.Length; i++)
{
if (wordRegister.ContainsKey(ArrLoop[i]))
{
// here is where im having trouble
wordRegister[ArrLoop[i]] = count += 1;
}
else
{
wordRegister.Add(ArrLoop[i], count);
}
}
foreach (DictionaryEntry pair in wordRegister)
{
Console.WriteLine($"Key : {pair.Key} ; Forekomster : {pair.Value}");
}
Console.ReadLine();
}
public static String WordString()
{
Document doc = new Document();
doc.LoadFromFile(#"C:\Users\xxx\Desktop\2.g\IDO.docx");
String textString = doc.GetText();
return textString;
}
public static string[] TextSplitter()
{
string s = WordString();
String[] wordArr = s.Split();
return wordArr;
}
}
}
You don't need the count variable. You are incrementing a counter common to all entries.
Try this instead to keep the counts distinct from each other:
wordRegister[ArrLoop[i]] += 1;

How do you get a program to pick a different string from a list each time rather than writing the same string over and over again?

I am having an issue with picking randomly from a list. When I run the program, it keeps writing the same string over and over again when I want it to come up with a different random string each time. Here is an example:
using System;
using System.Collections.Generic;
namespace List
{
class Program
{
static void Main(string[] args)
{
var letterList = new List<string>
{
"A","B","C","D"
};
Random r = new Random();
int letterListIndex = r.Next(letterList.Count);
for (int i = 0; i < 10; i++) {
Console.WriteLine(letterList[letterListIndex]);
}
}
}
}
For Example: When I run this code it would write "B" 10 times. I want it to come up with 10 different letters every time. I know that you could just write:
int letterListIndex1 = r.Next(letterList.Count);
int letterListIndex2 = r.Next(letterList.Count);
int letterListIndex3 = r.Next(letterList.Count);
Console.WriteLine(letterList[letterListIndex1]);
Console.WriteLine(letterList[letterListIndex2]);
Console.WriteLine(letterList[letterListIndex3]);
But I wanted to know if there was an easier way to do so.
Thanks.
You should put your letterListIndex variable inside for loop
for (int i = 0; i < 10; i++) {
int letterListIndex = r.Next(letterList.Count);
Console.WriteLine(letterList[letterListIndex]);
}
Otherwise you get the same index every time.
You need to put
int letterListIndex = r.Next(letterList.Count);
inside the for loop.
you can use below code
for (int i = 0; i < 10; i++) {
Console.WriteLine(letterList[r.Next(letterList.Count)]);
}

No errors in Visual Studio, Code Executes but something not right C# (updated! problem in String.Insert)

I'm writing a program for schoolwork, that's supposed to generate a 10 000 "movie" list. A single "movie" consist of a string in a form "moviename year director". I say "movie" because movie name and director are supposed to be randomly generated with letters from a-z.
I wrote the following logic to generate one such "movie". Movie name and director are random letter combination in length between 4-10 charachters. Code gives no errors in visual studio, executes, but shows blank. If I wrote correctly, then this code should generate one such string and print it, yet console is blank.
Do while loop is there to check if, however unlikely, there is a double item in the List (this is for when I do the 10 000 version).
In short, I dont understand what am I doing wrong?
using System;
using System.Collections.Generic;
using System.Linq;
namespace Experiment
{
class Program
{
static void Main(string[] args)
{
Movies();
Console.ReadKey();
}
public static void Movies()
{
List<string> movieList = new List<string>();
bool check = false;
do
{
string movie = "";
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
movie.Insert(0, " ");
movie.Insert(0, Convert.ToString(GetYear()));
movie.Insert(0, " ");
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
if (movieList.Contains(movie))
{
check = false;
}
else
{
movieList.Add(movie);
check = true;
}
} while (check == false);
Console.WriteLine(movieList[0]);
}
public static Random _random = new Random();
public static char GetLetter()
{
int num = _random.Next(0, 26);
char let = (char)('a' + num);
return let;
}
public static int GetNum()
{
int num = _random.Next(4, 11);
return num;
}
public static int GetYear()
{
int num = _random.Next(1920, 2020);
return num;
}
}
}
Strings are immutable, so calling the Insert() method on the movie string doesn't do anything to the current movie variable. Instead it returns the new string.
You are however better off changing the movie type from string to StringBuilder, which is a dynamically allocated buffer of characters, so your example becomes:
using System;
using System.Text;
using System.Collections.Generic;
namespace sotest
{
class Program
{
static void Main(string[] args)
{
Movies();
Console.ReadKey();
}
public static void Movies()
{
List<string> movieList = new List<string>();
bool check = false;
do
{
StringBuilder movie = new StringBuilder();
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
movie.Insert(0, " ");
movie.Insert(0, Convert.ToString(GetYear()));
movie.Insert(0, " ");
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
if (movieList.Contains(movie.ToString()))
{
check = false;
}
else
{
movieList.Add(movie.ToString());
check = true;
}
} while (check == false);
Console.WriteLine(movieList[0]);
}
public static Random _random = new Random();
public static char GetLetter()
{
int num = _random.Next(0, 26);
char let = (char)('a' + num);
return let;
}
public static int GetNum()
{
int num = _random.Next(4, 11);
return num;
}
public static int GetYear()
{
int num = _random.Next(1920, 2020);
return num;
}
}
}
The problem is that you are using movie.Insert incorrectly.
If you read the documentation for String.Insert it says
https://learn.microsoft.com/en-us/dotnet/api/system.string.insert?view=netframework-4.8
Returns a new string in which a specified string is inserted at a
specified index position in this instance
public string Insert (int startIndex, string value);
So it returns a new String, it does not amend the existing one. So you would need to do.
movie = movie.Insert(0, Convert.ToString(GetYear()));
However, I must also say that using String.Insert in this way is not the best approach.
You should instead look at using the StringBuilder class. It is very efficient when you want to amend strings (which are immutable objects).
You might want to read parts of this to help you understand. If you scroll down then it also suggests StringBuilder.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/
Insert() method is used to return a new string from the specified string at a specified index position. In your case, you are not capturing the updated string.
The best approach to solve this is through using StringBuilder object. Please note that StringBuilder object is much efficient rather than playing with immutable string.

Sorting a Queue

I have to simulate a process scheduler using SRTN algorithm and im having trouble within a certain part.
I have a queue of a custom class called 'Process' I need to sort it based on a a field called 'last_prediction'. My code works most of the time, but if you look at time:19 of my output, the output in the ready queue is wrong (it should be: 1004(1.5) 1002(2) 1003(2)).
Here is my code:
int count = ReadyQueue.Count;
// Copy Queue into Vector
ArrayList temp = new ArrayList();
for (int i = 0; i < count; i++)
{
Process p = (Process)ReadyQueue.Dequeue();
temp.Add(p);
}
// Sort Vector
for (int i = 0; i < count; i++)
{
double min = ((Process)temp[i]).last_prediction;
for (int j=i+1; j<count; j++)
{
if ( ((Process)temp[j]).last_prediction < min )
{
min = ((Process)temp[j]).last_prediction;
Process dummy = (Process)temp[j];
temp[j] = temp[i];
temp[i] = dummy;
}
}
}
// Copy Vector back into Queue
for (int i = 0; i < count; i++)
{
Process p = (Process)temp[i];
ReadyQueue.Enqueue(p);
}
EDIT: ok, im trying to use ICompare, similar to what you gave hughdbrown.Now i get a different error:
public class Process
{
public int process_id;
public int arrival_time;
public int total_time;
public int avg_burst;
public int actual_burst;
public int last_burst; // SRTN
public double last_prediction; // SRTN
public int io_delay;
public int context_switch_delay;
public class ProcessSort : IComparer
{
public int Compare(object x, object y)
{
var a = x as Process;
var b = y as Process;
double aNum = a.last_prediction;
double bNum = b.last_prediction;
return Compare(aNum, bNum);
}
}
}
this is the error i get now:
Unhandled Exception: System.InvalidOperationException: Failed to compare two elements in the array. ---> System.NullReferenceException: Object reference not set to an instance of an object.
I would use a real sorting routine on this array, not a hand-crafted insertion/bubble sort. Add a comparison function to your object.
I'd also use a templatized data collection, not ArrayList. You might be interested in using this C# PriorityQueue code from my website. That has Queue semantics and maintains items in a sorted order.
Later: Your IComparable code would be something like this:
public class Process : IComparable
{
int last_prediction;
public int CompareTo(object obj)
{
Process right = obj as Process;
return this.last_prediction.CompareTo(right.last_prediction);
}
}
Later still: here is a complete test program that has a sortable Process. Tested in Mono on ubuntu.
using System;
using System.Collections.Generic;
using System.Text;
namespace Comparer
{
public class Process : IComparable
{
int last_prediction;
public Process(int p)
{
this.last_prediction = p;
}
public int CompareTo(object obj)
{
Process right = obj as Process;
return this.last_prediction.CompareTo(right.last_prediction);
}
public int Prediction { get { return this.last_prediction; } }
}
class MainClass
{
public static void Main (string[] args)
{
List<Process> list = new List<Process>();
for (int i = 0; i < 10; i++)
list.Add(new Process(10 - i));
System.Console.WriteLine("Current values:");
foreach (Process p in list)
System.Console.WriteLine("Process {0}", p.Prediction);
list.Sort();
System.Console.WriteLine("Sorted values:");
foreach (Process p in list)
System.Console.WriteLine("Process {0}", p.Prediction);
}
}
}
Have you considered using the ArrayList.Sort method instead of attempting to write your own sort?
Here's how I would sort the Process objects. Let's use a List<Process> instead of an ArrayList so that we don't have to keep casting it back and forth. I haven't done much with queues in C# so I'm afraid I can't help much with those. And please note that this code is untested. :)
int count = ReadyQueue.Count;
// Copy Queue into Vector
List<Process> listProcesses = new List<Process>();
for(int i = 0; i < count; i++)
{
Process p = (Process)ReadyQueue.Dequeue();
listProcesses.Add(p);
}
// Sort Vector
listProcesses.Sort(CompareProcessesByPrediction);
// Copy Vector back into Queue
foreach(Process p in listProcesses)
ReadyQueue.Enqueue(p);
private static int CompareProcessesByPrediction(Process proc1, Process proc2)
{
//if they're both not-null, figure out which one is greatest/smallest.
//otherwise just pick the one that isn't null
if(proc1 == null)
return proc2 == null ? 0 : -1;
else
return proc1 == null ? 1 : proc1.last_prediction.CompareTo(proc2.last_prediction);
}
yea.. use arraylist.sort
If ur array only got numbers, create a new number array coz.. arraylist.sort for string has some problem.
and use arraylist.sort
take the number of the position you want and convert back to string if u want..

Problem with delegates in C#

In the following program, DummyMethod always print 5. But if we use the commented code instead, we get different values (i.e. 1, 2, 3, 4). Can anybody please explain why this is happenning?
delegate int Methodx(object obj);
static int DummyMethod(int i)
{
Console.WriteLine("In DummyMethod method i = " + i);
return i + 10;
}
static void Main(string[] args)
{
List<Methodx> methods = new List<Methodx>();
for (int i = 0; i < 5; ++i)
{
methods.Add(delegate(object obj) { return DummyMethod(i); });
}
//methods.Add(delegate(object obj) { return DummyMethod(1); });
//methods.Add(delegate(object obj) { return DummyMethod(2); });
//methods.Add(delegate(object obj) { return DummyMethod(3); });
//methods.Add(delegate(object obj) { return DummyMethod(4); });
foreach (var method in methods)
{
int c = method(null);
Console.WriteLine("In main method c = " + c);
}
}
Also if the following code is used, I get the desired result.
for (int i = 0; i < 5; ++i)
{
int j = i;
methods.Add(delegate(object obj) { return DummyMethod(j); });
}
The problem is that you're capturing the same variable i in every delegate - which by the end of the loop just has the value 5.
Instead, you want each delegate to capture a different variable, which means declaring a new variable in the loop:
for (int i = 0; i < 5; ++i)
{
int localCopy = i;
methods.Add(delegate(object obj) { return DummyMethod(localCopy); });
}
This is a pretty common "gotcha" - you can read a bit more about captured variables and closures in my closures article.
This article will probably help you understand what is happening (i.e. what a closure is): http://blogs.msdn.com/oldnewthing/archive/2006/08/02/686456.aspx
If you look at the code generated (using Reflector) you can see the difference:
private static void Method2()
{
List<Methodx> list = new List<Methodx>();
Methodx item = null;
<>c__DisplayClassa classa = new <>c__DisplayClassa();
classa.i = 0;
while (classa.i < 5)
{
if (item == null)
{
item = new Methodx(classa.<Method2>b__8);
}
list.Add(item);
classa.i++;
}
foreach (Methodx methodx2 in list)
{
Console.WriteLine("In main method c = " + methodx2(null));
}
}
When you use the initial code it creates a temporary class in the background, this class holds a reference to the "i" variable, so as per Jon's answer, you only see the final value of this.
private sealed class <>c__DisplayClassa
{
// Fields
public int i;
// Methods
public <>c__DisplayClassa();
public int <Method2>b__8(object obj);
}
I really recommend looking at the code in Reflector to see what's going on, its how I made sense of captured variables. Make sure you set the Optimization of the code to ".NET 1.0" in the Option menu, otherwise it'll hide all the behind scenes stuff.
I think it is because the variable i is put to the heap (it's a captured variable)
Take a look at this answer.

Categories

Resources