Why does this button object cause a stack overflow? [duplicate] - c#

This question already has answers here:
How is a StackOverflowException detected?
(6 answers)
Closed 1 year ago.
void OpenClean( int i, int j) {
bnt[i, j] = new Button();
if (!bnt[i,j].Enabled) return;
bnt[i, j].Enabled = false;
bnt[i,j].BackColor = Color.Aquamarine;
bnt[i, j].Text = data[i, j].ToString();
if (data[i, j] == 0) {
if (i > 0 && j > 0 && data[i - 1, j - 1] == 0) OpenClean(i - 1, j - 1);
if (i > 0 && data[i - 1, j] == 0) OpenClean(i - 1, j);
if (i > 0 && j < 8 && data[i - 1, j + 1] == 0) OpenClean(i - 1, j + 1);
if (j > 0 && data[i, j - 1] == 0) OpenClean(i, j - 1);
if (j < 8 && data[i, j + 1] == 0) OpenClean(i, j + 1);
if (i < 8 && j > 0 && data[i + 1, j - 1] == 0) OpenClean(i + 1, j - 1);
if (i < 8 && data[i + 1, j] == 0) OpenClean(i + 1, j);
if (i < 8 && j < 8 && data[i + 1, j + 1] == 0) OpenClean(i + 1, j + 1);
}
It tells me Process is terminated due to StackOverflowException.
Because of my limited experience,I didn't find an effective way.
This question has been bothering me for a long time.
Please help or try to give me some ideas how to solve it.
Thanks in advance!

You never set data[i, j] to a value other than 0. Therefore, the recursion never stops. You must set the inspected cells to a different value so that they will not get inspected again and again and again ...
You also create a new button with bnt[i, j] = new Button(); instead of inspecting the existing button. Therefore you will never see a button with Enabled == false, since the default value for Enabled is true.
It is better to base the logic on the model (i.e. data[,]) and to use the UI (the controls) only for display.
const int Inspected = -1;
void OpenClean( int i, int j) {
...
if (data[i, j] == 0) {
data[i, j] = Inspected; // <====== This stops the endless recursion!
if (i > 0 && j > 0 && data[i - 1, j - 1] == 0) OpenClean(i - 1, j - 1);
...
}
}
And, btw., you are testing for 0 twice. Before calling OpenClean and inside OpenClean again. Just do it once.
void OpenClean( int i, int j) {
if (data[i, j] == 0) { // This one does the job.
data[i, j] = Inspected; // <======
// bnt[i, j] = new Button(); <=== drop this!
// if (!bnt[i,j].Enabled) return; <=== drop this! We test data instead.
bnt[i, j].Enabled = false;
bnt[i,j].BackColor = Color.Aquamarine;
bnt[i, j].Text = data[i, j].ToString();
if (i > 0 && j > 0) OpenClean(i - 1, j - 1);
if (i > 0 ) OpenClean(i - 1, j);
if (i > 0 && j < 8) OpenClean(i - 1, j + 1);
if ( j > 0) OpenClean(i, j - 1);
if ( j < 8) OpenClean(i, j + 1);
if (i < 8 && j > 0) OpenClean(i + 1, j - 1);
if (i < 8 ) OpenClean(i + 1, j);
if (i < 8 && j < 8) OpenClean(i + 1, j + 1);
}
}
If you do the range test at the beginning of OpenClean, you can get rid of all the other range tests.
void OpenClean( int i, int j) {
if (i > 0 && i < 8 && j > 0 && j < 8 && data[i, j] == 0) { // This one does the job.
data[i, j] = Inspected; // <======
bnt[i, j].Enabled = false;
bnt[i,j].BackColor = Color.Aquamarine;
bnt[i, j].Text = data[i, j].ToString();
OpenClean(i - 1, j - 1);
OpenClean(i - 1, j);
OpenClean(i - 1, j + 1);
OpenClean(i, j - 1);
OpenClean(i, j + 1);
OpenClean(i + 1, j - 1);
OpenClean(i + 1, j);
OpenClean(i + 1, j + 1);
}
}
Using the C# 9.0 patterns, you can simplify the condition somewhat
if (i is > 0 and < 8 && j is > 0 and < 8 && data[i, j] == 0)

Related

Don't know what IndexOutOfRangeException means

I am attempting an insertion sort algorithm in c# and struggling to fix this error message:
"System.IndexOutOfRangeException' occurred in algorithmsAssignment.exe"
As soon as it reaches the while loop, the code breaks and gives me the message. Any help would be appreciated
(I have had to do string.compare as I'm using a 2D array string.
static void insertionSort(int columnSort, bool accendingOrder)
{
int column = columnSort - 1;
int i, j;
for (i = 1; i < dataArray.GetLength(1); i++)
{
string key = dataArray[column, i];
j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && string.Compare(dataArray[column, j - 1],
dataArray[j, column]) > 0)
{
dataArray[column, j + 1] = dataArray[column, j];
j = j - 1;
}
dataArray[column, j + 1] = key;
}
}
In your first for iteration: ( i = 1 )
string key = dataArray[column, i];
j = i - 1;
// J value is 0
while (j >= 0 && string.Compare(dataArray[column, j - 1], //Here, j - 1 = -1, since j = 0
....
....
I bet there is your index out of range, since index -1 can't exist.
Cheers
You will get error for i=1 because you have this conditions:
j = i - 1; //j=0 for i=1
and wrong condition in while loop
while (j >= 0 && string.Compare(dataArray[column, j - 1],
dataArray[j, column]) > 0)
this condition in while loop dataArray[column, j - 1] will throw IndexOutOfRange exception because
j-1=-1 for j=0

how can i type cast my array object to string in my view

/*this is my ontroller and array object has to be converted to string type so that i can perform my action
namespace taskmvc.Controllers
{
public class dogController : Controller
{
public ActionResult dog()
{
string[,] array = new string[7, 14]
{
{"D","G","O","O","D","D","O","D","G","O","O","D","D","O"},
{"O","D","O","O","G","G","G","D","O","D","G","O","G","G"},
{"O","G","O","G","D","O","O","D","G","O","O","D","D","D"},
{"D","G","D","O","O","O","G","G","O","O","G","D","G","O"},
{"O","G","D","G","O","G","D","G","O","G","G","O","G","D"},
{"D","D","D","G","D","D","O","D","O","O","G","D","O","O"},
{"O","D","G","O","G","G","D","O","O","G","G","O","O","D"}
};
ViewData["f"] = array;
return View(array);
}
}
}
/* this is my view here you can see that my array is a string
#{
ViewBag.Title = "dog";
}
<h2>dog</h2>
#{
string [,] array = ViewData["f"];
int i, j;
var n = 7;
var m = 14;
}
#for (i = 0; i <n;i++)
{
for(j=0;j<m;j++)
{
if (array[i, j] == "D")
{
if (j + 1 < m && array[i, j + 1] == "O")//
{
if(j + 1 < m && array[i,j+1]=="G")
{
}
}
}
else if (i+1<n && j + 1 < m && array[i + 1, j + 1] == "O")
{
if (i + 1 < n && j + 1 < m && array[i + 1, j + 1] == "G")
{
}
}
else if (i - 1 >0 && array[i - 1, j] == "O")//
{
if (i - 1 > 0 && array[i - 1, j] == "G")
{
}
}
else if (j-1>0 && array[i, j - 1] == "O")
{
if (j - 1 > 0 && array[i, j - 1] == "G")
{
}
}
else if(i + 1< n && array[i +1, j ] == "O")
{
if (i + 1 < n && array[i + 1, j] == "G")
{
}
}
else if(i+1<n && j-1>0 && array[i+1,j-1]=="O")
{
if (i + 1 < n && j - 1 > 0 && array[i + 1, j - 1] == "G")
{
}
}
else if (i-1>0&&j-1>0&&array[i-1,j-1]=="O")
{
if (i - 1 > 0 && j - 1 > 0 && array[i - 1, j - 1] == "G")
{
}
}
else if(i-1>0&&j+1<m&&array[i-1,j+1]=="O")
{
if (i - 1 > 0 && j + 1 < m && array[i - 1, j + 1] == "G")
{
}
}
}
}
j < 14, so max(j) = 13, j+1 = 14, array[i,j+1] is illegal.
j+1 is out of range.
Check before:
if(j+1 < 14 && array[i,j+1]=="G")
...and in many other places.
Also, replace 7 and 14 with variables. For example:
var n = 7;
var m = 14;
if(j+1 < m && array[i,j+1]=="G")
Since you are checking array[i+1, j+1] , array[i - 1, j],array[i+1,j-1], then you should use the following loop :
for (i = 1; i <6i++)
{
for(j=1;j<13;j++)
{
...
You are trying to access array[i,j+1]
for last index j=13, and j+1 will be "14"
So you are accessing array[i,14]
As this memory location not allocated to to your array, You will get index out of range exception.
Try looping from i=0; j=0; and avoid situation like array[i-1,j-1]. (Boundary Conditions)

C# LevenshteinDistance algorithm for spellchecker

Hi i'm using the levenshtein algorithm to calculate the difference between two strings, using the below code. It currently provides the total number of changes which need to be made to get from 'answer' to 'target', but i'd like to split these up into the types of errors being made. So classifying an error as a deletion, substitution or insertion.
I've tried adding a simple count but i'm new at this and don't really understand how the code works so not sure how to go about it.
static class LevenshteinDistance
{
/// <summary>
/// Compute the distance between two strings.
/// </summary>
public static int Compute(string s, string t)
{
int n = s.Length;
int m = t.Length;
int[,] d = new int[n + 1, m + 1];
// Step 1
if (n == 0)
{
return m;
}
if (m == 0)
{
return n;
}
// Step 2
for (int i = 0; i <= n; d[i, 0] = i++)
{
}
for (int j = 0; j <= m; d[0, j] = j++)
{
}
// Step 3
for (int i = 1; i <= n; i++)
{
//Step 4
for (int j = 1; j <= m; j++)
{
// Step 5
int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
// Step 6
d[i, j] = Math.Min(
Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
d[i - 1, j - 1] + cost);
}
}
// Step 7
return d[n, m];
}
}
Thanks in advance.

Levenshtein distance c# count error type

I found this bit of code that computes Levenshtein's distance between an answer and a guess:
int CheckErrors(string Answer, string Guess)
{
int[,] d = new int[Answer.Length + 1, Guess.Length + 1];
for (int i = 0; i <= Answer.Length; i++)
d[i, 0] = i;
for (int j = 0; j <= Guess.Length; j++)
d[0, j] = j;
for (int j = 1; j <= Guess.Length; j++)
for (int i = 1; i <= Answer.Length; i++)
if (Answer[i - 1] == Guess[j - 1])
d[i, j] = d[i - 1, j - 1]; //no operation
else
d[i, j] = Math.Min(Math.Min(
d[i - 1, j] + 1, //a deletion
d[i, j - 1] + 1), //an insertion
d[i - 1, j - 1] + 1 //a substitution
);
return d[Answer.Length, Guess.Length];
}
But I need a way to do a count for the amount of times each error occurs. Is there an easy way to implement that?
Seems like you could add counters for each of the operations:
if (Answer[i - 1] == Guess[j - 1])
d[i, j] = d[i - 1, j - 1]; //no operation
else
{
int del = d[i-1, j] + 1;
int ins = d[i, j-1] + 1;
int sub = d[i-1, j-1] + 1;
int op = Math.Min(Math.Min(del, ins), sub);
d[i, j] = op;
if (i == j)
{
if (op == del)
++deletions;
else if (op == ins)
++insertions;
else
++substitutions;
}
}

Damerau - Levenshtein Distance, adding a threshold

I have the following implementation, but I want to add a threshold, so if the result is going to be greater than it, just stop calculating and return.
How would I go about that?
EDIT: Here is my current code, threshold is not yet used...the goal is that it is used
public static int DamerauLevenshteinDistance(string string1, string string2, int threshold)
{
// Return trivial case - where they are equal
if (string1.Equals(string2))
return 0;
// Return trivial case - where one is empty
if (String.IsNullOrEmpty(string1) || String.IsNullOrEmpty(string2))
return (string1 ?? "").Length + (string2 ?? "").Length;
// Ensure string2 (inner cycle) is longer
if (string1.Length > string2.Length)
{
var tmp = string1;
string1 = string2;
string2 = tmp;
}
// Return trivial case - where string1 is contained within string2
if (string2.Contains(string1))
return string2.Length - string1.Length;
var length1 = string1.Length;
var length2 = string2.Length;
var d = new int[length1 + 1, length2 + 1];
for (var i = 0; i <= d.GetUpperBound(0); i++)
d[i, 0] = i;
for (var i = 0; i <= d.GetUpperBound(1); i++)
d[0, i] = i;
for (var i = 1; i <= d.GetUpperBound(0); i++)
{
for (var j = 1; j <= d.GetUpperBound(1); j++)
{
var cost = string1[i - 1] == string2[j - 1] ? 0 : 1;
var del = d[i - 1, j] + 1;
var ins = d[i, j - 1] + 1;
var sub = d[i - 1, j - 1] + cost;
d[i, j] = Math.Min(del, Math.Min(ins, sub));
if (i > 1 && j > 1 && string1[i - 1] == string2[j - 2] && string1[i - 2] == string2[j - 1])
d[i, j] = Math.Min(d[i, j], d[i - 2, j - 2] + cost);
}
}
return d[d.GetUpperBound(0), d.GetUpperBound(1)];
}
}
This is Regarding ur answer this: Damerau - Levenshtein Distance, adding a threshold
(sorry can't comment as I don't have 50 rep yet)
I think you have made an error here. You initialized:
var minDistance = threshold;
And ur update rule is:
if (d[i, j] < minDistance)
minDistance = d[i, j];
Also, ur early exit criteria is:
if (minDistance > threshold)
return int.MaxValue;
Now, observe that the if condition above will never hold true! You should rather initialize minDistance to int.MaxValue
Here's the most elegant way I can think of. After setting each index of d, see if it exceeds your threshold. The evaluation is constant-time, so it's a drop in the bucket compared to the theoretical N^2 complexity of the overall algorithm:
public static int DamerauLevenshteinDistance(string string1, string string2, int threshold)
{
...
for (var i = 1; i <= d.GetUpperBound(0); i++)
{
for (var j = 1; j <= d.GetUpperBound(1); j++)
{
...
var temp = d[i,j] = Math.Min(del, Math.Min(ins, sub));
if (i > 1 && j > 1 && string1[i - 1] == string2[j - 2] && string1[i - 2] == string2[j - 1])
temp = d[i,j] = Math.Min(temp, d[i - 2, j - 2] + cost);
//Does this value exceed your threshold? if so, get out now
if(temp > threshold)
return temp;
}
}
return d[d.GetUpperBound(0), d.GetUpperBound(1)];
}
You also asked this as a SQL CLR UDF question so I'll answer in that specific context: you best optmiziation won't come from optimizing the Levenshtein distance, but from reducing the number of pairs you compare. Yes, a faster Levenshtein algorithm will improve things, but not nearly as much as reducing the number of comparisons from N square (with N in the millions of rows) to N*some factor. My proposal is to compare only elements who have the length difference within a tolerable delta. On your big table, you add a persisted computed column on LEN(Data) and then create an index on it with include Data:
ALTER TABLE Table ADD LenData AS LEN(Data) PERSISTED;
CREATE INDEX ndxTableLenData on Table(LenData) INCLUDE (Data);
Now you can restrict the sheer problem space by joining within an max difference on lenght (eg. say 5), if your data's LEN(Data) varies significantly:
SELECT a.Data, b.Data, dbo.Levenshtein(a.Data, b.Data)
FROM Table A
JOIN Table B ON B.DataLen BETWEEN A.DataLen - 5 AND A.DataLen+5
Finally got it...though it's not as beneficial as I had hoped
public static int DamerauLevenshteinDistance(string string1, string string2, int threshold)
{
// Return trivial case - where they are equal
if (string1.Equals(string2))
return 0;
// Return trivial case - where one is empty
if (String.IsNullOrEmpty(string1) || String.IsNullOrEmpty(string2))
return (string1 ?? "").Length + (string2 ?? "").Length;
// Ensure string2 (inner cycle) is longer
if (string1.Length > string2.Length)
{
var tmp = string1;
string1 = string2;
string2 = tmp;
}
// Return trivial case - where string1 is contained within string2
if (string2.Contains(string1))
return string2.Length - string1.Length;
var length1 = string1.Length;
var length2 = string2.Length;
var d = new int[length1 + 1, length2 + 1];
for (var i = 0; i <= d.GetUpperBound(0); i++)
d[i, 0] = i;
for (var i = 0; i <= d.GetUpperBound(1); i++)
d[0, i] = i;
for (var i = 1; i <= d.GetUpperBound(0); i++)
{
var im1 = i - 1;
var im2 = i - 2;
var minDistance = threshold;
for (var j = 1; j <= d.GetUpperBound(1); j++)
{
var jm1 = j - 1;
var jm2 = j - 2;
var cost = string1[im1] == string2[jm1] ? 0 : 1;
var del = d[im1, j] + 1;
var ins = d[i, jm1] + 1;
var sub = d[im1, jm1] + cost;
//Math.Min is slower than native code
//d[i, j] = Math.Min(del, Math.Min(ins, sub));
d[i, j] = del <= ins && del <= sub ? del : ins <= sub ? ins : sub;
if (i > 1 && j > 1 && string1[im1] == string2[jm2] && string1[im2] == string2[jm1])
d[i, j] = Math.Min(d[i, j], d[im2, jm2] + cost);
if (d[i, j] < minDistance)
minDistance = d[i, j];
}
if (minDistance > threshold)
return int.MaxValue;
}
return d[d.GetUpperBound(0), d.GetUpperBound(1)] > threshold
? int.MaxValue
: d[d.GetUpperBound(0), d.GetUpperBound(1)];
}

Categories

Resources