-3

I have looked into this some, and I can't find a good way to do this. I have functional code but I want to clean it up without using manual counting or breaks. I looked at LINQs TakeWhile function, but that is insufficient in this case. Here is an example of a working implementation of a functional equivalent:

bool foo(List<strings> stringList, int counter)//assume this list has, like, 10 elements, and counter=3, idk
{
    ...

    bool found= false;
    for(int i=0; i<stringList.Count && !found; i++)
    {
        if(stringlist[i].length < 2 || counter >=6)
            found=true;
        counter++;
    }

    ...

    return found
}

And I want to replace it with some "magic" function like this:

bool foo(List<strings> stringList, int counter)//assume this list has, like, 10 elements, and counter=3, idk
{
    ...

    bool found= false;
    foreachwhile(string s in stringList while !found)
    {
        if(s.length < 2 || counter >=6)
            found=true;
        counter++;
    }

    ...

    return found
}

Is there some function that can do this already, and if not how can I write one in C#... or get close? I often find I need to do this, and yes, I know that:

bool foo(List<strings> stringList, int counter)//assume this list has, like, 10 elements, and counter=3, idk
{
    ...

    bool found= false;
    foreach(string s in stringList)
    {
        if(found)
            break;
        if(s.length < 2 || counter >=6)
            found=true;
        counter++;
    }

    ...

    return found
}

technically works too, but I would like to avoid breaks in my code. Is there any other clean way to accomplish this in c#?

EDIT: I am seeing a lot of confusion about what I am asking here. I would prefer a way to add a very clear check if we should break to the foreach statement itself if possible, or a way to write my own version of foreach that accepts a parameter that only continues while a condition is true, such as the path isn't found yet.

Also, I am sorry for not being clear, but the biggest reason I want to avoid break is so that I am not jumping out of the loop at an arbitrary point. In this respect return is just as undesirable.

I also have nothing against Linq, I tried to use it, but I found it does not produce the functionality that I am looking for:

bool foo(List<strings> stringList, int counter)//assume this list has, like, 10 elements, and counter=3, idk
{
    bool found= false;
    foreach(string s in stringlist.Takewhile(x=> (!found)))
    {
        if(s.length < 2 || counter >=6)
            found=true;
        counter++;
    }
    return found
}

is different logic from the above because TakeWhile executes first and returns an empty set causing the foreach loop to have nothing to loop over. If someone has another way to write this, I would genuinely love to hear it as syntactically it looks extremely clean.

Firestar9114
  • 432
  • 4
  • 9
  • what is that you are trying to achieve (logic) – Priyesh Kumar Sep 16 '18 at 05:12
  • 4
    Why do you want to avoid `break`? It is perfectly valid part of the language, and is not frowned upon like `goto` was in older languages. Its sole purpose in life is to do what you are looking for. – Richardissimo Sep 16 '18 at 05:13
  • Yeah you should describe what the method is supposed to do. Your code and pseudo code examples aren’t clear. – maccettura Sep 16 '18 at 05:13
  • Refusing to use `break` here sounds like an arbitrary restriction. Kind of like "I want to check a condition, but without using `if`". -- They all (loops, too) boil down to "jumps" in the execution flow. Except their allowed uses are much clearer defined than those of a `goto`, so they're considered "safe" to use. – Corak Sep 16 '18 at 05:37
  • I like to be very explicit about where I exit loops and functions and thus I try to always have exactly one `return` at the bottom of the function, and I avoid using `break` in loops. Although not as bad as `goto` sice they have a defined exit point to jump to, they still cause jumps in the code at arbitrary points in functions and loops which I want to avoid for clarity of flow in my code as much as possible. – Firestar9114 Sep 16 '18 at 05:46
  • @Priyesh Kumar This is just a simplified example of other complex programs where I need something like this. Recently it came up when writing a path-finding algorithm that loops through nodes in a network in nested loops and I want to skip the remainder of the algorithm once I have found the path. There are other places where I could use such a function too, though none come to mind at the moment, so I decided to ask if there was such a thing or if one could be made. – Firestar9114 Sep 16 '18 at 05:49
  • 4
    Then the question is, why do you have such big and complex methods/functions that using a `break` makes them unclear? If you can't look at a method and immediately figure out how it works, then *that's* the problem. Not a `break` or another `return`. That also includes, if the method doesn't "fit on the screen", it's probably too big and should be split up. If it's short enough but you still take some time to understand it, probably the naming is not clear enough. There are a lot of other things to focus on to make code more readable! Avoiding `break/return` should be pretty low on that list. – Corak Sep 16 '18 at 06:03
  • You can also add comment to your code: `//this foreach loop halts when found == true` – Masoud Keshavarz Sep 16 '18 at 06:57
  • @Masoud Keshavarz I might do that, if I can't find a better way. That comment with a loop that breaks might be cleaner than a for loop. I would still like to avoid breaking though if possible. – Firestar9114 Sep 16 '18 at 07:16

3 Answers3

1

The fact that there is other code before and after the loop indicates that you should Refactor your code to separate the concerns. Your code will improve if you separate pieces of logic into different places. Long methods with closely-coupled logic is not reusable and is hard to maintain.

For example your method could return the logical combination of several different pieces of logic, each of which is delegated to a different method. The piece of logic which is shown could be delegated to another method as follows.

I would use break, since it is designed to do exactly what you want, and there is nothing wrong with it. But for your specific example, you could also avoid break like this...

bool foo_part2(List<strings> stringList, int counter)
{
    foreach (string s in stringList)
    {
        if(s.length < 2 || counter >=6)
            return true;
        counter++;
    }
    return false;
}

...And the original method would look something like this..

bool foo(List<strings> stringList, int counter)
{
    return foo_part1(stringList,counter) ||
        foo_part2(stringList,counter) ||
        foo_part3(stringList,counter);
}

(You may need to use &&s instead of ||, depending on the required logic.) With properly named methods (rather than this "foo" example), this can be very easy to read, and will avoid you having to look into the function to see what it's doing.

However, to conclude... there is a mistake in the question. You state "I also have nothing against Linq, I tried to use it, but I found it does not produce the functionality that I am looking for... because TakeWhile executes first". That isn't correct. Which means that you didn't try it yourself, and that you didn't understand the answer you were given to your previous question 20 minutes before asking this one. It is using a predicate, so the value is evaluated each time through the iteration, which is exactly what you want. So this entire question is flawed, and if you had just tried it yourself (before even asking your previous question), you would have worked that out yourself.

I refer the OP to the question of How much research effort is expected of Stack Overflow users?.

Richardissimo
  • 5,596
  • 2
  • 18
  • 36
  • This definitely works, and is solid, but I also have other code before and after this loop which needs to run regardless of breaking from the loop early. I will update my queston to reflect this. – Firestar9114 Sep 16 '18 at 05:39
  • Although I agree that refactoring code to separate it into logical partitions is good practice, I don't think every 4 lines of code really need to be separate functions as this can make it annoying to constantly have to be looking up functions. I typically use #region to separate code chunks like this into logical, collapsible partitions while maintaining a linear flow of code. It's especially nice because #region can reduce comments by using good region names. – Firestar9114 Sep 16 '18 at 06:08
  • 3
    @Firestar9114. So to summarise: You don't want to use `break`, `return`, Linq; and you don't want to Refactor your code into logically separate functionality. And the irony that you need to use `#region`s (which I would say [are frowned upon by StyleCop](https://stackoverflow.com/questions/4545754/why-stylecop-rule-does-not-allow-using-region-in-code)) to know what chunks of your code do is passing you by. I'm sorry, I don't think I (or anyone else) can help you. – Richardissimo Sep 16 '18 at 06:16
  • 2
    Yeah... regions inside methods... the bane of my existence. I had to work with a gigantic mess of legacy code just like that for years now. It's the single worst thing you could do for readability. Sure, *now* you know what you're doing. And on each little *change* you know what you do. But going back to a piece you haven't touched for a year or so? Good luck making sense of that! And if you ever consider handing that code to someone else, make sure to *not* give him your address... Quite often a new method for even **one** line of code does wonders for readability, let alone reusability. – Corak Sep 16 '18 at 06:24
  • @Richardissimo I have no problem with Linq whatsoever, but it does not give the same logic that I want, and the linq examples here utilize returns. – Firestar9114 Sep 16 '18 at 07:11
  • @Corak ... so you would rather make functions for one line of code??? All that does is force programmers to got and look up function, after function, after function. Clearly having some functions is a great way to break up large complex systems, but in some cases it does make sense to divide a maybe 30 line function into say three logical sections. I am not claiming them to be a complete alternative to all functions, that would be ridiculous, but in some cases it can create cleaner code where you have to look up less references. – Firestar9114 Sep 16 '18 at 07:16
  • 2
    @Firestar9114 - Sure! If it makes things easier to understand. For example calling a method called `NextYear()` is (imho) easier to understand than directly writing `DateTime.Now.AddYears(1).Year`. And If I saw a call in code like `if (NextYearIsLeapYear()) { ... }`, I wouldn't even need to look into the implementation of that to see: `return IsLeapYear(NextYear());`. And even then, do you *need* to further look into these methods to see what they do? No! Because you're not interested in the implementation details of those. Because you work on a higher level of abstraction already. – Corak Sep 16 '18 at 09:43
  • 1
    @Firestar9114 - the Single Responsibility Principle (S in SOLID) not only applies to libraries and classes, but also to methods. – Corak Sep 16 '18 at 09:50
  • @Firestar9114 I've added the final two paragraphs to my answer, which I think you should read. – Richardissimo Sep 16 '18 at 15:42
0

Not sure what your code is doing; but following achieves the same thing using Linq Any.

bool foo(List<string> stringList, int counter)//assume this list has, like, 10 elements, and counter=3, idk
            {
                return stringList.Any(s =>
                {
                    var found = s.Length < 2 || counter >= 6;
                    counter++;
                    return found;
                });            
            }
loopedcode
  • 4,863
  • 1
  • 21
  • 21
  • 1
    Great username for answering this question by the way ;) – Richardissimo Sep 16 '18 at 05:24
  • I apologize, I should have made it clear there was other code in the function. I stripped out a lot when I simplified it into a clear example, and have updated my question to reflect this. – Firestar9114 Sep 16 '18 at 05:53
  • I have no problem with linq, I actually thought `TakeWhile` might do what I wanted, but in this case it would execute all at once and fail immediately and the outer loop would not run in the foreach. – Firestar9114 Sep 16 '18 at 06:26
0

If you insist to don't use break the alternative way is to use delegates, although using break is best practice.

...

bool found= false;
Action Search = () =>
{
    foreach(string s in stringList)
    {
        if(s.length < 2 || counter >=6)
            found=true;
            return;
        counter++;
    }
}

Search();

...

Don't use this code. Use break.

Masoud Keshavarz
  • 2,166
  • 9
  • 36
  • 48
  • Instead of the delegate, you can use a local method as well (C# 7). – Jeppe Stig Nielsen Sep 16 '18 at 06:20
  • Sorry, but the reason I want to avoid break is so that I am not jumping out of the loop at an arbitrary point. In this respect return is just as undesirable. I would prefer a way to add a very clear check to the loop itself if possible, or a way to write my own version of `foreach` that accepts a parameter that only continues while a condition is true, such as the path isn't found yet. – Firestar9114 Sep 16 '18 at 06:20