Recursion experimenting - c#

I'm trying to experiment with recursion so as to grasp the concept. It is language agnostic, so the same concept applies to both C# and Java.
I've got a TreeView which has a number of nodes. I would like to iterate through every single node and count the ones which satisfy a certain condition. If at any time the condition is not satisfied, I would like the algorithm to finally return -1.
Each TreeViewItem will only be considered if it has a Tag named "Condition" (there are 3 types of TreeViewItems in all - I will only consider the "Condition" ones).
Once a TreeViewItem is found to be of the "Condition" type, I would like to check to see that it satisfies a certain condition. As I mentioned before, even if only one TreeViewItem does not satisfy the condition, I want the algorithm to return -1 in the end.
If the algorithm does not return -1, I want it to return the amount of valid conditions which it has found - i.e. an integer is to be incremented each time a condition is successfully passed, and the final count is returned at the end.
This is what I've tried so far:
private int CountConditions(TreeViewItem item)
{
int conditionCount = 0;
foreach (TreeViewItem child in item.Items)
{
int previousCount = CountConditions(child);
if (previousCount == -1)
{
return -1;
}
else
{
return conditionCount += previousCount;
}
}
if (item.Tag.Equals("Condition"))
{
if (/*Condition is not satisfied*/)
{
return -1;
}
else
{
return conditionCount++;
}
}
else
{
return conditionCount;
}
}
My current algorithm does infact return -1 if a condition is not satisfied, however if conditions are satisfied it just returns 0, rather than the amount of valid conditions.

you use
return conditionCount++;
which is bad practice. for good reason. what happens here is
a)return conditionCount (which you set to zero)
b)increment conditionCount
b never happens as its after the return statement, so you always pass 0 to your next recursion step.
you can use
return ++conditionCount;
or the much better
conditionCount++;
return conditionCount;

It is not a trivial recursion, since you have to handle both the error condition, and the normal condition. If you didn't have the error condition, and only needed to count the number of condition nodes, you could write:
private int CountConditions(TreeViewItem item)
{
int currentCondition = CalculateCondition(item)
int childCounts = 0;
foreach (TreeViewItem child in item.Items)
{
int childCount = CountConditions(child);
childCounts += childCount;
}
return currentCondition + conditionCounts
}
private int CalculateCondition(TreeViewItem item)
{
if (item.Tag.Equals("Condition"))
return 1;
else
return 0;
}
But to handle the error, you have to have two checks for the error condition, one for the current node, and one for the child nodes, and return immediately, if you encounter the condition:
int error = -1
private int CountConditions(TreeViewItem item)
{
int currentCondition = CalculateCondition(item)
if (currentCondition == error) // new
return error; // new
int childCounts = 0;
foreach (TreeViewItem child in item.Items)
{
int childCount = CountConditions(child);
if (childCount == error) // new
return error; // new
childCounts += childCount;
}
return currentCondition + conditionCounts
}
private int CalculateCondition(TreeViewItem item)
{
if (item.Tag.Equals("Condition"))
if (((item.Header as StackPanel).Children[2] as TextBox).Text.Equals("")) // new
return error; // new
else // new
return 1;
else
return 0;
}

You should return conditionCount; only at the end of the function. Only the return -1; have to be in the middle of the function.

return stops the whole function at once and returns a value. That's not what you want. You want to accumulate values and return the sum when you have finished.

I think you could simplify your code by using exceptions. What you do is you throw an exception when the condition fails which you can then catch in another function which starts of the recursion. This will automatically unwind the stack and abort the computation. The function which catches the exception can then return whatever you want it to.
Apart from this all you would have to do in the recursion is accumulate 1 for each passed condition.

Related

Is there really a valid code-path where this function won't return a value?

I have the below function that iterates over a list of workers, invoking their DoStuff() method. If the first worker fails, I try the next one until I'm out of workers. If they all fail, I re-throw the last exception.
// workers is an IList<>.
public object TryDoStuff()
{
for (int i = 0; i < workers.Count; i++)
{
try
{
return worker[i].DoStuff();
}
catch
{
if (i == workers.Count - 1)
{
throw; // This preserves the stack trace
}
else
{
continue; // Try the next worker
}
}
}
}
When compiling, I get an error that "not all code paths return a value" for this function. Although I can silence the error by adding an explicit return after the for loop, I'm doubting the compiler is accurate here as I don't see how the for loop will be escaped without either returning or re-throwing an exception. And if an exception is re-thrown, not returning a value is valid.
What am I missing? Is csc unable to reason about the conditional in the catch block?
Yes
If there is an exception thrown on the last index and count isn't what you expect it to be (unlikely yet possible)
Or as RAM pointed out if Count is zero
In this case, the static analysis and subsequent compiler error is very justified
As previously mentioned, if workers is empty (Count is 0), there's no valid return path.
There's also another race condition (depending on the full context, obviously) where workers is not empty, an exception is thrown on an element, there are still elements to iterate in workers, but after evaluating if (i == workers.Count - 1) and before the continue statement executes, another thread removes elements from workers (or changes the entire workers variable to a new instance).
In that scenario, the for condition will return false on the next iteration unexpectedly and you'll fall out of the loop with no return statement for the method.
public object TryDoStuff()
{
for (int i = 0; i < workers.Count; i++)
{
try
{
return worker[i].DoStuff();
}
catch
{
if (i == workers.Count - 1)
{
throw; // This preserves the stack trace
}
else
{
// XXX If workers is changed by another thread here. XXX
continue; // Try the next worker
}
}
}
}
I wrote as a comment for you:
What will be happen if the count of the workers list items be
zero?
It seems this is the compiler question and it dose not do more research about your code! :)
Actually this reason is enough for compiler to show the bellow error to you
not all code paths return a value
When the compiler encounters with a loop in the whole of the body of a method it assume that the loop condition cause that the loop body be ignored then it expected any value out of the loop too.
Yes, even if we set the condition of the loop at the way that the loop be executed!
Proof:
With error:
public static object TryDoStuff()
{
var result =0;
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Add 100 unit");
result += 100;
return result;
}
//Console.WriteLine("last line");
// return result;
}
Without error:
public static object TryDoStuff()
{
var result =0;
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Add 100 unit");
result += 100;
// return result; you can un-comment this line too
}
Console.WriteLine("last line");
return result;
}

C# checkBox Logic .NET

I am trying to write this Program that gives the user the checkBox and then the user chooses One or Two and the Program will return the answer accordingly. but the program is not accepting the && Operator. if the user checks two boxes the program is only returning the result of the first checkBox not the second. it is not even adding.
This is the Code:
private double Oillube()
{
//checking if the Oil is checked
if (oilChbox.Checked)
{
return OIL_CHANGE;
}
//checking if the lube is checked
if (lubeChbox.Checked)
{
return LUBE_JOB;
}
//if they are both checked
if(lubeChbox.Checked && oilChbox.Checked)
{
//Creating Variables sum to hold the value of the two
double sum;
sum = OIL_CHANGE + LUBE_JOB;
//returning sum
return sum;
}
else
{
//just returning to get ride off the red line under the Method
return 0;
}
}
Since the AND condition is more restrictive than the others, you should check it first. Like this:
private double Oillube()
{
//if they are both checked
if(lubeChbox.Checked && oilChbox.Checked)
{
//Creating Variables sum to hold the value of the two
double sum;
sum = OIL_CHANGE + LUBE_JOB;
//returning sum
return sum;
}
//checking if the Oil is checked
if (oilChbox.Checked)
{
return OIL_CHANGE;
}
//checking if the lube is checked
if (lubeChbox.Checked)
{
return LUBE_JOB;
}
//just returning to get ride off the red line under the Method
return 0;
}
The lubeChbox.Checked && oilChbox.Checked condition needs to go first, as it is the more specific of the two.
The way you have it now if either lubeChbox.Checked is true, or oilChbox.Checked is true it will return before reaching the lubeChbox.Checked && oilChbox.Checked condition, meaning there is no situation in which it could ever reach the condition unless both were false.
This is why it is typically a bad idea for people to use return in this manner unless they fully understand the flow of the code they want to achieve.
Here's the corrected code:
private double Oillube()
{
//if they are both checked
if(lubeChbox.Checked && oilChbox.Checked)
{
//Creating Variables sum to hold the value of the two
double sum;
sum = OIL_CHANGE + LUBE_JOB;
//returning sum
return sum;
}
//checking if the Oil is checked
if (oilChbox.Checked)
{
return OIL_CHANGE;
}
//checking if the lube is checked
if (lubeChbox.Checked)
{
return LUBE_JOB;
}
//just returning to get ride off the red line under the Method
return 0;
}
Alternatively you can use tertiary operators for this situation like so:
private double Oillube()
{
return (lubeChbox.Checked ? LUBE_JOB : 0) + (oilChbox.Checked ? OIL_CHANGE : 0);
}
A tertiary operator works the same as an if statement only they work inline.
So this:
(lubeChbox.Checked ? LUBE_JOB : 0)
is equivalent to this pseudo-code:
if(lubeChbox.Checked) { LUBE_JOB } else { 0 }
Dotnetfiddle example
It's because if the first checkbox is checked, then first if statement is true, so you are returning value, thus ending the method.
You should replace your if statements, last one should become first, as it is most restrictive.

Unreachable code detected in the for loop

I'm trying to find out if a number is prime or not. But I've got an error of "unreachable code detected", which I think is effecting the error of "not all code paths return a value". The error seems to occur in the for loop at i++. Can anyone help me please?
static void Main(string[] args)
{
Console.WriteLine(isPrime(10));
}
public static bool isPrime(int n)
{
for (int i = 2; i < n; i++)
{
if (n % i == 0)
{
return false;
}
return true;
}
}
"Unreachable code detected" means that some code can never be executed. Consider:
int something()
{
if (true)
return 1;
else
return 2; //Obviously we can never get here
}
"Not all code paths return a value" means that you've defined a method with a return value (like "bool" in your example) and there is some way for the method to execute without returning a value.
Consider:
int something(bool someBool)
{
if (someBool)
return 1;
//if someBool == false, then we're not returning anything. Error!
}
Your code has two problems:
You have return true inside the for loop (outside of any conditional). Because return immediately exits the function (returning control to the caller) the i++ statement of the for loop will never get executed (hence your bug). You likely intended for that to be outside the for loop.
Another problem with that being in the loop is that the loop is not guaranteed to execute. If the n passed was 2 or less, you would skip the loop entirely, and there is no return statement in that case. This isn't allowed (since you always need to return a value from a non-void function) so you get a compiler error.
Below is an example of how to get this return working with a for loop and embedded If condition.
private bool WinOneLevelOne()
{
//For loop to check all the items in the winOne array.
for (int i = 0; i < winOne.Length; i++)
{
//If statement to verify that all the gameobjects in the array are yellow.
if (winOne[i].gameObject.GetComponent<MeshRenderer>().material.color != Color.yellow)
{
//Keeps the boolean at false if all the gameobjects are not yellow.
return false;
}
}
return true;

C#... Not all code paths return a value

I am trying to create a Collection with properties and their respective accessors.
Here is my code:
class SongCollection : List<Song>
{
private string playedCount;
private int totalLength;
public string PlayedCount
{
get
{
foreach (Song s in this)
{
if (s.TimesPlayed > 0)
{
return s.ToString();
}
}
}
}
public int TotalLength
{
get
{
foreach (Song s in this)
{
int total = 0;
total += s.LengthInSeconds;
}
return total;
}
}
}
I'm receiving the error at the "get" point. It tells me that not all code paths return a value... What exactly does this mean, and what am I missing?
Firstly, the reason you're getting that message is that if this is empty, then the code within the foreach block (which is where the required return statement is) would never be executed.
However, your TotalLength() function would always return the length of the first Song, as you're declaring your variable, setting its value, then returning it within the foreach block. Instead, you'd need to do something like this:
int totalLength = 0;
foreach(Song s in this)
{
total += s.LengthInSeconds;
}
return totalLength;
Your PlayedCount function suffers from similar issues (if the collection is empty or contains no elements whose TimesPlayed property is greater than 0, then there would be no way for it to return a value), so judging by your comment you could write it this way:
public int PlayedCount()
{
int total = 0;
foreach(Song s in this)
{
if (s.TimesPlayed > 0)
{
total++;
}
}
return total;
}
It means just as it says, not all code paths return a value.
In this case, if your list is empty, then it cannot call return. In a foreach, there must be at least one item for the code to execute. Now, maybe you know that the list will always contain a value, but the compiler can't know that
What would your method return if this did not evaluate?
if (s.TimesPlayed > 0)
{
return s.ToString();
}
try using an else to return an empty string or something
The fact that 'this' could have no songs- in that case the loops will not execute at all and there is no implicit return value in C#.
Furthermore, your getters don't really make sense unless you only ever had one song in the collection. You need something more like this:
public int TotalLength()
{
get
{
int total = 0;
foreach (Song s in this)
{
total += s.LengthInSeconds;
}
return total;
}
}
Finally, without knowing how you keep track of TimesPlayed for each individual song, I wouldn't know how to implement that getter, but I am sure you can figure it out with this much.

Return value from For loop

I have a listView in my app. I loop through the items to check which one is currently selected and then return a value. As all paths have to return a value I have to return a value outside the loop which overwrites the for loop return, how do I keep this without overwriting it after the loop?
public string GetItemValue()
{
for (int i = 0; i < listView1.Items.Count; i++)
{
if (listView1.Items[i].Checked == true)
{
return listView1.Items[i].Text; // I want to keep this value
}
}
// Without overwriting it with this but the compiler
// requires me to return a value here
return "Error";
}
Any help is most appreciated. Thanks.
P.S I have tried using break after the if but no luck.
On edit: bringing down my comment from above.
You don't need to worry about this. As soon as it hits the first return inside the loop, it will return immediately with that value. No code outside the loop is ever hit in that case.
Incidentally, this code would be cleaner:
public string GetItemValue()
{
foreach (var item in listView1.Items)
{
if (item.Checked) return item.Text;
}
throw new InvalidOperationException("No checked items found");
}
Exceptions are a more idiomatic way of handling errors, and the foreach loop is preferred to a for loop when you're just iterating over collections.
Also using LINQ, you can get even more concise:
public string GetItemValue()
{
return listView1.Items.Cast<ListViewItem>().Single(i => i.Checked).Text;
}
Well, your return outside of the loop, return "Error"; shouldn't get called based on your logic. Since return causes your method to immediately exit, the "Error" return will never happen, that is unless the code never steps into the if inside the loop.
All things considered, this may be an exceptional case in your code. Thus, throwing an exception may be the appropriate thing to do:
public string GetItemValue()
{
for (int i = 0; i < listView1.Items.Count; i++)
{
if (listView1.Items[i].Checked == true)
{
return listView1.Items[i].Text; // I want to keep this value
}
}
throw new InvalidOperationException("Did not find value expected.");
}
Exceptions are usually thrown to indicate that a bug exists in code. A "Hey, that should really not be happening." The application stops, and hopefully the user gets an opportunity to contact support to help you reproduce it.
Based on your comment:
When I run it, it just returns the error text...
That means your check in your if statement is not succeeding.
if (listView1.Items[i].Checked == true)
Which means that none of your items in your ListView are checked.
That's the kind of situations where you are probably better of throwing an exception in order to signal the exceptional situation:
public string GetItemValue()
{
for (int i = 0; i < listView1.Items.Count; i++)
{
if (listView1.Items[i].Checked == true)
{
// Here you are leaving the GetItemValue method
// and the loop stops
return listView1.Items[i].Text;
}
}
// if we get that far it means that none of the items of
// the select list was actually checked => we are better of
// reporting this to the caller of the method
throw new Exception("Please select a value");
}
The return in your for loop isn't overwritten -- the method will return the value in the loop if your conditions are met. Execution of the method ends immediately upon reaching a return statement.
If your method is returning "Error", then I'd recommend looking at your code in a debugger, because it's reaching the end of the loop and returning the value "Error".
If you're returning a value in the loop, it should not reach the return that's outside of the loop. I would check to make sure that the loop is finding the selected item.
The other option is to create a local variable to hold the return value:
string returnValue = "Error";
for (int i = 0; i < listView1.Items.Count; i++)
{
if (listView1.Items[i].Checked == true)
{
returnValue = listView1.Items[i].Text;
break;
}
}
return returnValue;
Lastly, you might also consider returning an exception when no selections are found, and handling the exception from the calling method.
The return "Error" bit will not overwrite your loop return value. When a return is hit, the function exits, so when a value that is selected is found, the function will spit out your data and stop.
The compiler requires that all paths of the function return a value. The compiler cannot know before hand whether your inner loop if condition will be met. You can cache the value at a variable and return this in the end of the function e.g. :
public string GetItemValue()
{
string temp = null;
for (int i = 0; i < listView1.Items.Count; i++)
{
if (listView1.Items[i].Checked == true)
{
temp = listView1.Items[i].Text; // I want to keep this value
break;
}
}
return temp; // Without overwriting it with this but the compiler requires me to return a value here
}
Actually, there is no need for a loop here:
return (listView1.SelectedItems.Count > 0)
? listView1.SelectedItems[0].Text
: "Error";
But, as it was said, the original question is misleading since return does not override values. You might be thinking about assignment, instead of return. In this case a working code can look like this:
string ret = "Error";
foreach(var item in listView1.Items)
{
if(item.Checked) { ret = item.Text; break; }
}
return ret;

Categories

Resources