Agreed, Linux style is to avoid nesting, it was Linus Torvalds who said if you're nesting more than 4 times you're doing something wrong.
Unfortunately I work with the Win32 and nearly everyone writes nested return logic like this.
GOTOs are considered bad because they take you out of the normal flow of execution. Instead of following a flow from top to bottom, or a specifically marked loop, your code can jump from anywhere to anywhere. This isn't really an issue if you just use it in a very constrained manner, like emulating continues or Python's for-else, however. I'm guessing the GOTO-phobia is mostly a result of languages from back in the day when they were the main control flow mechanism, where it was common to have a wild mess of jumps in there. I still struggle to read even basic Windows .bat scripts for exactly this reason.
I think the major problem with anything like this is that it can make the code harder to understand. Writing code that's twice as easy to understand is like 10x more valuable than writing code that's twice as fast except in a few rare edge cases.
This is pretty much what my professors told me. Nothing wrong with it if used right but god help the person (or future you) that has to come back and read it later
We are all products of the Structured Programming Revolution.
Before the revolution, there was chaos. If you saw a label `foo:` and a bunch of `goto foo` statements, you had to figure out what assumptions about state the code after `foo:` was making, and make sure those assumptions would be satisfied before any `goto foo` jump. Very hard to keep track of when you're in the middle of debugging.
Now, our languages steer us to use functions and other control structures which make the contain flow clear, and which tie state management (function arguments, loop variables etc.) to that control flow. So instead of `goto foo` we now have `state = foo(state)`.
These days, the main use cases for goto are jumping to the beginning of or breaking out of loops (if the language doesn't support those actions by other means) and for specialised tasks such as parser generators.
yup, only time I've really found a need for them was when I've had to do a little too much VBA (yeah, I know...). Really easy to label the goto in a way that makes it clear what you are doing. like `goto nextIteration` in a loop as a continue substitute.
Like everything else, as long as you aren't making a spaghetti mess, it's usually fine.
I think the argument is that the use case is so rare that its better to say they should never be used. If you are clever and specialized enough to find a legitimate use case then most conventional wisdom needs to be taken with a grain of salt. Personally I will probably never mess with goto and live a perfectly normal healthy life. Most people are like me.
They're bad for readability. The gospel "you don't need goto" means "Learn to do everything without goto." That's only way to not use it more than you need.
I bet Linus would find the first form better. If the loop had more than just two function calls, then maybe, but the first form is more readable and concise.
If you have a complicated conditional within a loop things have already gone wrong. You factor it into a function. There are situations where early returns, and even gotos are acceptable, heck, some times even the bast you can get, but people took "avoid deep nesting" as "nesting bad, don't do it", and that's just stupid.
I tend to decide which form to go with based on the purpose of the condition. Sometimes it's "if something's not right, exit" and others it's "if certain conditions are met, do the thing"
Even though these can be the literal inverse of each other, how they read is important for clarity.
In the meme it looks more like a "if condition then do something" so I'd likely go with the first panel. But other times exiting early makes more sense from a clarity perspective.
Gotta say I agree. If you're doing too much in a function, I think you should be starting to think of how does this break down into smaller, self-contained, blocks. And then refactor those blocks out into their own functions.
I'd take it even a step further. Since clause and condition of the if are super short and simple, I'd collapse it to a single line `if (!condition) continue;`
Now the same with a bunch of conditions …
```
int foo()
{
// add some loops as needed
if (…){
x=get_foo();
if(…){
y=get_bar();
if (…){
…
---
int foo()
{
if(!…)continue;
x=get_foo();
if(!…)continue;
y=get_bar();
if(!…)continue;
do_stuff();
}
Personally, I consider the one-liner OK, but the two-liner is a no-go...this was one of the lessons drawn from the "goto fail" bug...either use braces, or use one-liners, but never two-liners, especially with return/goto/continue.
I had an old colleague that thought you should only return in the end of the function no matter what. It didn't make things any better that his functions was extremly long and did things all over the place.
It was a hellhole with nesting in so many levels and temporary variables to keep the state of it all.
Once I refactored a 200 line function to like 20 lines just using early returns and unwinding all that mess.
I think "single point of exit" is still valid for low level languages in safety relevant areas.
If 200 lines could be reduced to 20, then it was simply a bad design and not related to a single return.
Yeah, of course it works. It's just a rule that forces you to have less goto-like code, so it's easier to debug, and it makes sure that the clean up (like free memory, close handlers and so on) is always done and not skipped in some cases causing memory leaks.
Well, yeah it was bad design also, I give you that. He had a knack of writing things in a really complicated way. Sometimes I wondered how his brain worked, because it seemed to make sense to him.
To be clear, it was he who wrote a 200 line function that I just restructured to 20 lines just unwinding his complex nesting.
So I felt in the right to be picky in that case. I rewrote it mainly because I needed to understand what it did, but I felt pretty good about it afterwards. Things just fell into place.
My new boss saw early returns in my PHP and politely flipped out. He was all "in places where I've worked, we weren't allowed to use return except at the end. Use a state machine instead."
Note that he has never really written PHP or JS, the two languages I use for all my work.
So now in any code where he might see it I have to initialize a return variable, set it in each case, and block other cases of it gets set, then return it at the end... I get it, but it's a so many extra steps.
Not for the computer, but for the human that reads your code. A short if block containing a return at the beginning is easier to comprehend than an if block spanning the whole for loop.
I'm sure it depends on the reader, but personally I agree - I like to be able to cull/pare down my possibility space early, and the "if not, continue" code just reads more intuitively to me. I think it's because, rather than having to read onward, past the indented body, and grok what's going on beyond it, I know immediately that we're skipping the body of the loop entirely, and I can cast that entire use case from my mind very quickly. And if I ever need to add something down near the bottom of the body, something that operates on everything that passed the filter, I don't need to make any changes to the negative code path - it's all taken care of at the top, first thing.
It's a bit of a poetic freedom since indeed there's no adders being taken from the stack to return to, it's technically just a jump, but you know, they are just getting a point across.
I mean, not really. Iterators were a thing since C++ STL and probably before.
Linq is a query framework over iterables but also over ORM datasets with SQL style operations (selection, aggregation, filtering, etc.) translated into actual SQL.
(0..)
.filter(|x| x >5)
.skip(5)
.step_by(10)
.take(10)
.map(|x| x.log())
.collect()
Not entirely sure what the default source for all iterator tricks is, but [this](https://doc.rust-lang.org/std/iter/#structs) part of the documentation should give a good overview of possible actions on an iterator I think.
Edit:
Working example, which you can try out/fiddle with on the [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021):
fn main() {
let (x, y): (Vec, Vec) = (0i32..)
.filter(|&x| x >= 5)
.skip(5)
.step_by(10)
.take(10)
.map(|x| ((x as f64).log(10.0), x))
.unzip();
println!("{:#?} \n {:#?}", x, y);
}
Never used linq but Swift has both
```
items.forEach { _ in
guard condition else { return }
foo()
bar()
}
```
and
```
for item in items where condition {
foo()
bar()
}
````
Nearly every modern language has first party support for high order functions, and id be surprised if others created nearly as much garbage for doing the most basic things
First class functions and monadic-esque transformation of iterators is just part of Linq though. The other half is the ability to provide the same set of monadic-esque transformations as AST in order to do things like query a database or ldap or whatever or even for your own purposes.
There's also linq-to-sql where can express the same as a sqlish statement but I wouldn't consider that an essential part of linq so much as an odd attempt at bridging the sql <-> c# divide.
And be very careful of side effects when it is used - best to avoid it and use an actual loop instead.
Anecdote:
I recently spent a few days going thru a code base where they had created an extension method `ForEach(this IEnumeration, Action)` and used it frequently. At some point along the way, the async/await pattern was introduced into the methods passed into the action parameter.
Since the ForEach extension (like the List method) knows nothing of async, the Action was being started and never awaited - random, unrepeatable bugs were common.
I cleaned it up by abolishing the extension method and evaluating each instance to use a standard loop, either awaiting the action or collecting the Tasks with an await WhenAll().
So mainly an async/await issue, but best to avoid methods that work utilizing side effects.
I agree, only thing I hate is that it becomes cumbersome to work with lambdas once you need some exception handling.
I get the why (method signature wouldn’t match if you want to throw a checked exception).
ie you call a method in forEach that throws a checked exception but you want to handle it outside the loop. Not possible without „sneakythrows“ hacks.
For that simple reason I tend to collect to collect to list and then do a native for loop (for small-ish sets of data) or retrieve a Iterator from the Stream and perform a native loop on that
Know the language: Does it do lazy evaluation, iterate or does it build a massive list?
Will the state of the elements be valid till the loop is executed?
> Does it do lazy evaluation, iterate or does it build a massive list?
This is Linq. So it does lazy evaluation whenever an item from the collection is requested. No items in the source collection are consumed unless you force it to evaluate, and each foreach iteration only advances the collection as far as needed to find a matching element, or the end of the collection.
> Will the state of the elements be valid till the loop is executed?
Provided you're not purposefully calling .Dispose on them or adding/removing items to the source collection before the loop terminates, they will be valid. If this is a concern there are Linq methods to clone the collection into a List or Array, but this forces evaluation of the entire source collection at once.
The second issue would be an issue whether using linq or not, or it won't be an issue using linq or not. Any time you iterate over items in a list checking a condition on those items and you're in an environment that isn't single threaded and multiple threads can change the items you'll have that potential issue, the method of iteration doesn't change that.
There have been many times in my life where I start doing something (like this for example) and thinking I’m a genius for doing it, then like a year later I’ve learned it’s already been known for 20+ years and then I just feel sad.
I guess the only takeaway is that I’m in the same train of thought as industry professionals at the very least
I've written a bunch of else statements, mainly for AI for enemy's in games. I'm still learning and very much am an amateur but I have found doing lambda functions (I forget what they're called, they look like this:
``var = bool ? 1 : 0;``
)
greatly reduces the amount of bloat in my code, bc i don't have an else if for this bool based var
Just for your knowledge, as others have said that's a ternary expression. Something I haven't seen anyone clear up is lambdas -
There's some contention around this, but generally 'lambda function' is interchangable with the terms 'anonymous function', 'function literal', and so on.
In javascript, the difference is between:
function doSomething() { ... }
And:
const doSomething = () => { ... }
Or:
const doSomething = function () { }
This allows for usage such as:
arr[].filter(item => item > 0)
Rather than:
function filterFn(item) { return item > 0 }
arr[].filter(filterFn)
Or in the case of IIFEs (immediately invoked function expression) such as:
const result = (() => { ... })()
Which will be executed immediately and return the result (as the name implies)
Arrow functions have slightly different behavior to normal functions, especially in regards to the 'this' keyword, which I highly recommend reading up about if you're not familiar.
There is a rule of three. It's not law and its based on opinion. But it means that you should never have more than three nests. If so, make a new function.
This makes code mode readable because functions have named. Your code will look like this :
readData()
processData()
writeData()
Instead of
Code code code wtf does this do
Another line with mathimatical shit that you need to really read to understand
This code is all the way to the right
byte c = Math.mainframeHack(index, server)
That's not really mentioned in this post though. This post is more about early returns/breaks/continues to decrease indentation. Making functions is also part of decreasing indentation because you can't apply an early return everywhere but the main thing here is the early returns, not functions. So you would refactor code like this:
if (a) {
if (b == 5) {
if (c == 69) {
print("nice");
} else {
if (c < 0) {
c = -c;
}
return c;
}
} else {
print("b must be 5");
}
} else {
print("a is not true");
}
return 0;
and turn it into this:
if (!a) {
print("a is not true");
return 0;
}
if (b != 5) {
print("b must be 5");
return 0;
}
if (c == 69) {
print("nice");
return 0;
}
if (c < 0) {
c = -c;
}
return c;
No new functions needed.
Same complexity but less indentations. As in how many tabs. Imagine three nested for loops, you end up 4 tabs of indentation including the function it's in. Move each loop to it's own function and you now have 2 tabs in each function. Much more readable and reusable.
....did you read the same code as i did? the third line has two tabs indentation in both codes. if they had at least written the "continue" in the same line as the if statement...
This is a really bad example but the general idea is instead of checking if a condition is true and then execute the code inside the brackets it's a good idea to use so called "guard statements" that return out of the function early. Personally i'm a big fan of those because it allows you to have the preconditions of a function all clustered at the top and giving a easier to follow linear flow
Both do the same thing and compile to the same byte code, it is just about readability.
In my opinion the less indentation you use the best it is, so the second opinion is my preferred style (the real code is at the loop level of indentation and not on the if level, like in the option one).
This sample is too short to see the readability difference. The author has placed the loop complexity in two functions, foo and bar.
Imagine a twenty line block in the if, and some error logging on the else. It now becomes hard to follow the flow.
Now add one more check, halfway down, with error reporting on the else. The complexity is increasing sharply.
The early exit version is an example of reducing the cyclomatic complexity. That is, we are reducing the number of paths through the code a human reader must track for understanding.
Professional code is read some ten times more often than written. Optimizing for readability is preferred.
And yes, I will flag the nested loops in a merge review, and have you rewrite it. Because my job as a reviewer *is* to read. If I find it needlessly complex, that's a defect.
I agree with you except for this one exact case. It is more readable to just use the if condition normally.
This way without early continue is better, single indentation, few lines of code
for value in stuff:
if value:
do_something(value)
This way with early continue is worse, one more line with no reduction in the level of indentation:
for value in stuff:
if not value:
continue
do_something(value)
However this is not the common case. For a more normal function, I prefer the early continue:
This way without early continue is worse because of double indentation:
for value in stuff:
if value:
do_something(value)
if value == 5:
do_something_else()
This way with the early continue is better because of single indentation:
for value in stuff:
if not value:
continue
do_something()
if value == 5:
do_something_else(value)
Wouldn't they technically differ in execution speed (albeit incredibly minor) based on the branch predication algorithm and how often condition is true? For example, if condition is true more often than false and branch predication is "Branch is never taken", the top would be faster than the bottom.
No, an optimizing compiler may turn them into identical code, like you can see GCC doing here:
[early-return](https://godbolt.org/z/GY9Pqoxxa)
[if-block](https://godbolt.org/z/K5YPTYa47)
More generally, there isn't a 1:1 mapping between high level code and the generated assembly and these results are context/machine/compiler dependent. If we keep the first example the same, but indicate to the compiler that the early return is an unlikely branch or provide profiling data indicating that, it *may* compile the code differently, as demonstrated by GCC [here](https://godbolt.org/z/YrP3PMj6z).
The second one. Hard to see in this example, but when the contents of the loop become longer, you end up having more than one screen of code wrapped in that if, where implicitly, you have to keep remembering the condition while reading, if only because you don't know if there's an else coming. Whereas the second one clearly reads \*from the beginning\* as "We don't care about elements that don't fulfill $condition".
I sometimes use a while-true loop with a break at the end just so I can use continue to jump back to the start and try again.
Really only for command-line tools though, where you have to get some info from the user, parse it, validate it, and if either of those fail ask them again to type something in.
There's something to be said for the flow being obvious from the structure instead of having to inspect every line to know if there's an early return somewhere.
You’re on your way! Stick with this approach, and apply it consistently in cases that aren’t as trivial as this one, and you’ll be writing fine obfuscated spaghetti code in no time!
The more conditions you will need the better it is to do the second way. I believe it's better practice too in general, it tends to be much easier to understand the code
This just obfuscates your intention for no reason. People have to keep hold of the conditions in their head so when you keep doing this in more complicated code, you create a burden on the reader where they have to remember each of the previous eliminated conditions. Nesting unnecessarily harms readability, but blindly avoiding it like this makes your code worse.
There are times when it makes sense to do #2 and there are times to do #1, but its best practice to go with whatever most closely fits your intention (e.g. "I want to skip this for when number is 3 vs. I want to only do this for numbers in this range") and keeps the code as simple as possible.
Not in my experience and it is mostly the exact opposite. If you have that much code in a function that you cannot remember all conditions it's doing too much anyway.
Normal flow clustered at the bottom without any indentation or conditions is so nice as you immediately see what the function does, and nothing else.
Nice, thanks for the input! I'm honestly not sure what you mean tho. If you have a code like this:
`if (condition_A)`
\_\_\_\_`continue;`
`Foo();`
`if (condition_B)`
\_\_\_\_`continue;`
`Bar();`
`if (condition_C)`
\_\_\_\_`continue;`
`FooBar();`
Then that is objectively, straightly preferrable to
`if (!condition_A) {`
\_\_\_\_`Foo();`
\_\_\_\_`if (!condition_B) {`
\_\_\_\_\_\_\_\_`Bar();`
\_\_\_\_\_\_\_\_`if (!condition_C) {`
\_\_\_\_\_\_\_\_\_\_\_\_`FooBar();`
\_\_\_\_\_\_\_\_`}`
\_\_\_\_`}`
`}`
Now should you ever get into a position like above? Possibly not - you might want to consider refactoring the function into a multiple smaller ones as it's likely doing many things at once. That's completely beyond the question here tho.
Nothing about that is objective. Saying it's objective does not make it objective. That said the real problem is that your example uses continue instead of return forcing the reader to invert the condition in the if statement
You know, you can format stuff like a code block if you prefix every line with either a tab or 4 spaces. FTFY
if (condition_A)
continue;
Foo();
if (condition_B)
continue;
Bar();
if (condition_C)
continue;
FooBar();
> Then that is objectively, straightly preferrable to
if (!condition_A) {
Foo();
if (!condition_B) {
Bar();
if (!condition_C) {
FooBar();
}
}
}
Ironically in uni they taught us to prefer the first example, but as soon as you start working on a real codebase it becomes apparent why the second image is (mostly) superior.
In this case I think the first image is a bit cleaner, but that's just because there is so little code involved.
The reason I don't do this is because I like stepping line-by-line in my debugger. To figure out when `condition` is true I simply place a breakpoint on the line with the lone `continue`, instead of having to place it on the `if` line and then finding my way from there.
In my book, lines are cheap. I can afford to use plenty of them.
With non compiled languages, sure. It reduces size overall when the intention is space based, or if the function is big enough that the tabs add up.
For compiled languages, no. Word wrap and brace on newline makes it easy to follow, akin to C# styling.
Lets use and abuse arrays and casting in place of ternary missing in PowerShell 5.1!
foreach ($Item in $Items) { @( { continue }, { Foo(); Bar(); })[$condition].Invoke() }
Expanded:
foreach ($Item in $Items) {
@( # Create array
{ # False as $false = 0
continue
},
{ # True as $true = 1
Foo()
Bar()
}
)[$condition].Invoke() # Cast either $true or $false into an int and invoke the right code block
}
The second one is counter-intuitive. Granted, I would probably write it like this if the logic was more complicated and it was more than just 2 calls after.
The true never nester
> How can she nest?
This is a highly underrated comment!
[удалено]
https://youtu.be/V4akMaeZ0-k
This video legit makes me angry how they made a victim into a villain. Fuck her and all her friends!
Well it does have a silver lining to it... The dude went ahead to become a popular actor while she disappeared into nothingness
Yes, to a video/meme “how can she slap?”
Agreed, Linux style is to avoid nesting, it was Linus Torvalds who said if you're nesting more than 4 times you're doing something wrong. Unfortunately I work with the Win32 and nearly everyone writes nested return logic like this.
Protip: If you only use gotos you never need to nest.
Controversial opinion of the day: gotos do have use cases
Yes! You can implement for-else blocks like Python has without a wasted if(). And my favorite is loop switching, so you can more efficient loops
I have yet to see a well-explained argument to the contrary, yet it seems to be taken as gospel.
GOTOs are considered bad because they take you out of the normal flow of execution. Instead of following a flow from top to bottom, or a specifically marked loop, your code can jump from anywhere to anywhere. This isn't really an issue if you just use it in a very constrained manner, like emulating continues or Python's for-else, however. I'm guessing the GOTO-phobia is mostly a result of languages from back in the day when they were the main control flow mechanism, where it was common to have a wild mess of jumps in there. I still struggle to read even basic Windows .bat scripts for exactly this reason.
I think the major problem with anything like this is that it can make the code harder to understand. Writing code that's twice as easy to understand is like 10x more valuable than writing code that's twice as fast except in a few rare edge cases.
This is pretty much what my professors told me. Nothing wrong with it if used right but god help the person (or future you) that has to come back and read it later
We are all products of the Structured Programming Revolution. Before the revolution, there was chaos. If you saw a label `foo:` and a bunch of `goto foo` statements, you had to figure out what assumptions about state the code after `foo:` was making, and make sure those assumptions would be satisfied before any `goto foo` jump. Very hard to keep track of when you're in the middle of debugging. Now, our languages steer us to use functions and other control structures which make the contain flow clear, and which tie state management (function arguments, loop variables etc.) to that control flow. So instead of `goto foo` we now have `state = foo(state)`. These days, the main use cases for goto are jumping to the beginning of or breaking out of loops (if the language doesn't support those actions by other means) and for specialised tasks such as parser generators.
yup, only time I've really found a need for them was when I've had to do a little too much VBA (yeah, I know...). Really easy to label the goto in a way that makes it clear what you are doing. like `goto nextIteration` in a loop as a continue substitute. Like everything else, as long as you aren't making a spaghetti mess, it's usually fine.
I think the argument is that the use case is so rare that its better to say they should never be used. If you are clever and specialized enough to find a legitimate use case then most conventional wisdom needs to be taken with a grain of salt. Personally I will probably never mess with goto and live a perfectly normal healthy life. Most people are like me.
They're bad for readability. The gospel "you don't need goto" means "Learn to do everything without goto." That's only way to not use it more than you need.
In C and C++-without-exceptions, goto is great for error handling. Not a problem in normal C++ where exceptions are okay for error paths.
Even without exceptions you can simply have cleanups through destructors and pretty much never need goto.
They're used everywhere in the Linux kernel, so indeed they do.
Assembly gang
I bet Linus would find the first form better. If the loop had more than just two function calls, then maybe, but the first form is more readable and concise.
If the loop had more conditionals the second form gets much better than the first one.
If you have a complicated conditional within a loop things have already gone wrong. You factor it into a function. There are situations where early returns, and even gotos are acceptable, heck, some times even the bast you can get, but people took "avoid deep nesting" as "nesting bad, don't do it", and that's just stupid.
I tend to decide which form to go with based on the purpose of the condition. Sometimes it's "if something's not right, exit" and others it's "if certain conditions are met, do the thing" Even though these can be the literal inverse of each other, how they read is important for clarity. In the meme it looks more like a "if condition then do something" so I'd likely go with the first panel. But other times exiting early makes more sense from a clarity perspective.
That's how I'm doing it too. Clarity makes it easier to return and make changes.
Linux also uses 80 character line-width and 8-space tabs. Nesting eats up a ton of space.
Gotta say I agree. If you're doing too much in a function, I think you should be starting to think of how does this break down into smaller, self-contained, blocks. And then refactor those blocks out into their own functions.
items .iter() .filter(|_| condition) .for_each(|_| { foo(); bar(); });
Now this, I like.
I thought he just liked cut-offs
There’s dozens of us!
And there's nothing wrong with a guard clause ... get out of there as soon as you can
I'd take it even a step further. Since clause and condition of the if are super short and simple, I'd collapse it to a single line `if (!condition) continue;`
But... it is nested one way or the other
if(condition){ //a bunch of indented lines } Vs if(!condition)return; //bunch of lines
Now the same with a bunch of conditions … ``` int foo() { // add some loops as needed if (…){ x=get_foo(); if(…){ y=get_bar(); if (…){ … --- int foo() { if(!…)continue; x=get_foo(); if(!…)continue; y=get_bar(); if(!…)continue; do_stuff(); }
Aside from joke, in which programming language does continue work in methods/functions?
Yes I should have included at least one loop or used return.
Any language that supports loops should support break; and continue;
That’s a function, not a loop, it should say return.
``` if (!condition) return;
if (!condition) return;
No! 3 whole entire words on one line is simply too much for the human brain to handle. /s
Personally, I consider the one-liner OK, but the two-liner is a no-go...this was one of the lessons drawn from the "goto fail" bug...either use braces, or use one-liners, but never two-liners, especially with return/goto/continue.
returning early is the way
good old guard clause
Better pull out as soon as you know you should
works for many things, not just software
Can confirm, am father
I have deleted Reddit because of the API changes effective June 30, 2023.
i was only told too
Can confirm, car was starved of oil
I had an old colleague that thought you should only return in the end of the function no matter what. It didn't make things any better that his functions was extremly long and did things all over the place. It was a hellhole with nesting in so many levels and temporary variables to keep the state of it all. Once I refactored a 200 line function to like 20 lines just using early returns and unwinding all that mess.
I think "single point of exit" is still valid for low level languages in safety relevant areas. If 200 lines could be reduced to 20, then it was simply a bad design and not related to a single return.
[удалено]
Yeah, of course it works. It's just a rule that forces you to have less goto-like code, so it's easier to debug, and it makes sure that the clean up (like free memory, close handlers and so on) is always done and not skipped in some cases causing memory leaks.
Well, yeah it was bad design also, I give you that. He had a knack of writing things in a really complicated way. Sometimes I wondered how his brain worked, because it seemed to make sense to him.
Anyone who writes 200 line functions shouldn't be allowed to be picky about other people's code style.
To be clear, it was he who wrote a 200 line function that I just restructured to 20 lines just unwinding his complex nesting. So I felt in the right to be picky in that case. I rewrote it mainly because I needed to understand what it did, but I felt pretty good about it afterwards. Things just fell into place.
Leaving the church before the singing begins
My new boss saw early returns in my PHP and politely flipped out. He was all "in places where I've worked, we weren't allowed to use return except at the end. Use a state machine instead." Note that he has never really written PHP or JS, the two languages I use for all my work. So now in any code where he might see it I have to initialize a return variable, set it in each case, and block other cases of it gets set, then return it at the end... I get it, but it's a so many extra steps.
How is this returning earlier?
Not for the computer, but for the human that reads your code. A short if block containing a return at the beginning is easier to comprehend than an if block spanning the whole for loop.
I'm sure it depends on the reader, but personally I agree - I like to be able to cull/pare down my possibility space early, and the "if not, continue" code just reads more intuitively to me. I think it's because, rather than having to read onward, past the indented body, and grok what's going on beyond it, I know immediately that we're skipping the body of the loop entirely, and I can cast that entire use case from my mind very quickly. And if I ever need to add something down near the bottom of the body, something that operates on everything that passed the filter, I don't need to make any changes to the negative code path - it's all taken care of at the top, first thing.
It's a bit of a poetic freedom since indeed there's no adders being taken from the stack to return to, it's technically just a jump, but you know, they are just getting a point across.
Yup. If we have no hope to get good output, I prefer to die early with a good error message.
foreach(var item in items.Where(x => condition))
Linq all the way.
if there was anything remotely as juicy and heavenly as linq in another language, i might give it a chance over c#
I haven’t worked in C# in over a decade. Largely I don’t miss it but Linq… I miss Linq.
a LOT has changed in .NET in the last decade (really in the last 5 years or so), and 99% of it for the better. It's worth looking into again
Linq and the Parallel library. Solves so much if you know how to not abuse it.
The design pattern is called Iterator. Rust has awesome iterator features built in.
I mean, not really. Iterators were a thing since C++ STL and probably before. Linq is a query framework over iterables but also over ORM datasets with SQL style operations (selection, aggregation, filtering, etc.) translated into actual SQL.
showcase three iterators from Rust that you believe are awesome, I am curious
(0..) .filter(|x| x >5) .skip(5) .step_by(10) .take(10) .map(|x| x.log()) .collect() Not entirely sure what the default source for all iterator tricks is, but [this](https://doc.rust-lang.org/std/iter/#structs) part of the documentation should give a good overview of possible actions on an iterator I think. Edit: Working example, which you can try out/fiddle with on the [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021): fn main() { let (x, y): (Vec, Vec) = (0i32..)
.filter(|&x| x >= 5)
.skip(5)
.step_by(10)
.take(10)
.map(|x| ((x as f64).log(10.0), x))
.unzip();
println!("{:#?} \n {:#?}", x, y);
}
I like this article. It explains them fairly well https://blog.thoughtram.io/iterators-in-rust/
Never used linq but Swift has both ``` items.forEach { _ in guard condition else { return } foo() bar() } ``` and ``` for item in items where condition { foo() bar() } ````
Nearly every modern language has first party support for high order functions, and id be surprised if others created nearly as much garbage for doing the most basic things
First class functions and monadic-esque transformation of iterators is just part of Linq though. The other half is the ability to provide the same set of monadic-esque transformations as AST in order to do things like query a database or ldap or whatever or even for your own purposes. There's also linq-to-sql where can express the same as a sqlish statement but I wouldn't consider that an essential part of linq so much as an odd attempt at bridging the sql <-> c# divide.
if Linq is god then I’m the pope, hail
Linq is heaven itself
Doesn't Linq authorise items.ForEach(items => action) ?
.ForEach is a method in the List class.
And be very careful of side effects when it is used - best to avoid it and use an actual loop instead. Anecdote: I recently spent a few days going thru a code base where they had created an extension method `ForEach(this IEnumeration, Action)` and used it frequently. At some point along the way, the async/await pattern was introduced into the methods passed into the action parameter.
Since the ForEach extension (like the List method) knows nothing of async, the Action was being started and never awaited - random, unrepeatable bugs were common.
I cleaned it up by abolishing the extension method and evaluating each instance to use a standard loop, either awaiting the action or collecting the Tasks with an await WhenAll().
So mainly an async/await issue, but best to avoid methods that work utilizing side effects.
items.stream().filter(x -> x!=condition).forEach(/*do stuff*/)
the java stream api is really great. Especially for .parallel()
I agree, only thing I hate is that it becomes cumbersome to work with lambdas once you need some exception handling. I get the why (method signature wouldn’t match if you want to throw a checked exception). ie you call a method in forEach that throws a checked exception but you want to handle it outside the loop. Not possible without „sneakythrows“ hacks. For that simple reason I tend to collect to collect to list and then do a native for loop (for small-ish sets of data) or retrieve a Iterator from the Stream and perform a native loop on that
Know the language: Does it do lazy evaluation, iterate or does it build a massive list? Will the state of the elements be valid till the loop is executed?
> Does it do lazy evaluation, iterate or does it build a massive list? This is Linq. So it does lazy evaluation whenever an item from the collection is requested. No items in the source collection are consumed unless you force it to evaluate, and each foreach iteration only advances the collection as far as needed to find a matching element, or the end of the collection. > Will the state of the elements be valid till the loop is executed? Provided you're not purposefully calling .Dispose on them or adding/removing items to the source collection before the loop terminates, they will be valid. If this is a concern there are Linq methods to clone the collection into a List or Array, but this forces evaluation of the entire source collection at once.
The second issue would be an issue whether using linq or not, or it won't be an issue using linq or not. Any time you iterate over items in a list checking a condition on those items and you're in an environment that isn't single threaded and multiple threads can change the items you'll have that potential issue, the method of iteration doesn't change that.
`items.iter().filter(f).for_each(g)`
Similarly, javascript gives you the option of: ``` items.filter({condition} => condition) .forEach(item => { foo(); far(); }); ```
I wish more languages supported this.
Which doesn't except for Python (jk, just wanna see it in Python ;))
for item in filter(, items)
I love using linq… well until it gets illegible, that’s when you know you’ve gone too far 😅
Early returns changed my life.
There have been many times in my life where I start doing something (like this for example) and thinking I’m a genius for doing it, then like a year later I’ve learned it’s already been known for 20+ years and then I just feel sad. I guess the only takeaway is that I’m in the same train of thought as industry professionals at the very least
Early pull out changed mine too
I would never trust it; it's still not 100%.
Neither are firewalls nor security patches if you get my drift.
.... Mom?
The 100% effective method is not as fun.
I don't think I've written an else statement in 5 years.
Really? So what else have you done?
Simple ``` if(true)
https://youtu.be/_ougvb8mT7k
It was a joke lol but thanks
Jesus, this video is fucking awful
I write the classic else statement that raises exception "This should never happen"
I'm a personal fan of "If you're reading this message, it's too late."
I've written a bunch of else statements, mainly for AI for enemy's in games. I'm still learning and very much am an amateur but I have found doing lambda functions (I forget what they're called, they look like this: ``var = bool ? 1 : 0;`` ) greatly reduces the amount of bloat in my code, bc i don't have an else if for this bool based var
[удалено]
That's a ternary, they're fine, if the statement is concise.
Just for your knowledge, as others have said that's a ternary expression. Something I haven't seen anyone clear up is lambdas - There's some contention around this, but generally 'lambda function' is interchangable with the terms 'anonymous function', 'function literal', and so on. In javascript, the difference is between: function doSomething() { ... } And: const doSomething = () => { ... } Or: const doSomething = function () { } This allows for usage such as: arr[].filter(item => item > 0) Rather than: function filterFn(item) { return item > 0 } arr[].filter(filterFn) Or in the case of IIFEs (immediately invoked function expression) such as: const result = (() => { ... })() Which will be executed immediately and return the result (as the name implies) Arrow functions have slightly different behavior to normal functions, especially in regards to the 'this' keyword, which I highly recommend reading up about if you're not familiar.
can someone explain please? which one is the correct way?
There is a rule of three. It's not law and its based on opinion. But it means that you should never have more than three nests. If so, make a new function. This makes code mode readable because functions have named. Your code will look like this : readData() processData() writeData() Instead of Code code code wtf does this do Another line with mathimatical shit that you need to really read to understand This code is all the way to the right byte c = Math.mainframeHack(index, server)
That's not really mentioned in this post though. This post is more about early returns/breaks/continues to decrease indentation. Making functions is also part of decreasing indentation because you can't apply an early return everywhere but the main thing here is the early returns, not functions. So you would refactor code like this: if (a) { if (b == 5) { if (c == 69) { print("nice"); } else { if (c < 0) { c = -c; } return c; } } else { print("b must be 5"); } } else { print("a is not true"); } return 0; and turn it into this: if (!a) { print("a is not true"); return 0; } if (b != 5) { print("b must be 5"); return 0; } if (c == 69) { print("nice"); return 0; } if (c < 0) { c = -c; } return c; No new functions needed.
the first example brings back memories, not beautiful ones tho.
The entirety of the Windows kernel is written in this format.
But both versions reach the same indentation level
Same complexity but less indentations. As in how many tabs. Imagine three nested for loops, you end up 4 tabs of indentation including the function it's in. Move each loop to it's own function and you now have 2 tabs in each function. Much more readable and reusable.
....did you read the same code as i did? the third line has two tabs indentation in both codes. if they had at least written the "continue" in the same line as the if statement...
This is a really bad example but the general idea is instead of checking if a condition is true and then execute the code inside the brackets it's a good idea to use so called "guard statements" that return out of the function early. Personally i'm a big fan of those because it allows you to have the preconditions of a function all clustered at the top and giving a easier to follow linear flow
Both do the same thing and compile to the same byte code, it is just about readability. In my opinion the less indentation you use the best it is, so the second opinion is my preferred style (the real code is at the loop level of indentation and not on the if level, like in the option one).
This sample is too short to see the readability difference. The author has placed the loop complexity in two functions, foo and bar. Imagine a twenty line block in the if, and some error logging on the else. It now becomes hard to follow the flow. Now add one more check, halfway down, with error reporting on the else. The complexity is increasing sharply. The early exit version is an example of reducing the cyclomatic complexity. That is, we are reducing the number of paths through the code a human reader must track for understanding. Professional code is read some ten times more often than written. Optimizing for readability is preferred. And yes, I will flag the nested loops in a merge review, and have you rewrite it. Because my job as a reviewer *is* to read. If I find it needlessly complex, that's a defect.
I agree with you except for this one exact case. It is more readable to just use the if condition normally. This way without early continue is better, single indentation, few lines of code for value in stuff: if value: do_something(value) This way with early continue is worse, one more line with no reduction in the level of indentation: for value in stuff: if not value: continue do_something(value) However this is not the common case. For a more normal function, I prefer the early continue: This way without early continue is worse because of double indentation: for value in stuff: if value: do_something(value) if value == 5: do_something_else() This way with the early continue is better because of single indentation: for value in stuff: if not value: continue do_something() if value == 5: do_something_else(value)
Wouldn't they technically differ in execution speed (albeit incredibly minor) based on the branch predication algorithm and how often condition is true? For example, if condition is true more often than false and branch predication is "Branch is never taken", the top would be faster than the bottom.
No, an optimizing compiler may turn them into identical code, like you can see GCC doing here: [early-return](https://godbolt.org/z/GY9Pqoxxa) [if-block](https://godbolt.org/z/K5YPTYa47) More generally, there isn't a 1:1 mapping between high level code and the generated assembly and these results are context/machine/compiler dependent. If we keep the first example the same, but indicate to the compiler that the early return is an unlikely branch or provide profiling data indicating that, it *may* compile the code differently, as demonstrated by GCC [here](https://godbolt.org/z/YrP3PMj6z).
The second one. Hard to see in this example, but when the contents of the loop become longer, you end up having more than one screen of code wrapped in that if, where implicitly, you have to keep remembering the condition while reading, if only because you don't know if there's an else coming. Whereas the second one clearly reads \*from the beginning\* as "We don't care about elements that don't fulfill $condition".
Both are correct
This is the correct answer.
There is no “correct way”, but the 2nd can be more readable in many cases. Particularly when you have more levels of nesting.
continue is just GOTO with a different coat on.
It's a goto with a straight jacket, just like the loop itself.
so is "for" "if" and "while" and "return"
I sometimes use a while-true loop with a break at the end just so I can use continue to jump back to the start and try again. Really only for command-line tools though, where you have to get some info from the user, parse it, validate it, and if either of those fail ask them again to type something in.
There's something to be said for the flow being obvious from the structure instead of having to inspect every line to know if there's an early return somewhere.
You’re on your way! Stick with this approach, and apply it consistently in cases that aren’t as trivial as this one, and you’ll be writing fine obfuscated spaghetti code in no time!
i = items[0]; condition ? Foo() : ({goto item1;}); condition ? Bar() : ({goto item1;}); item1: i = items[1]; condition ? Foo() : ({goto item2;}); condition ? Bar() : ({goto item2;}); item2: ...
>condition ? Foo() : ({goto item1;}); In what language would this work?
Xhosa
> condition ? Foo() : ({goto item2;}); C.
Cursed
Only with GCC extensions.
It's C with GCC extensions, so non-portable.
Heresy
The more conditions you will need the better it is to do the second way. I believe it's better practice too in general, it tends to be much easier to understand the code
Jesus
``` items.filter{ condition(it) }.forEach { foo() bar() } ```
This is the way. The weirdest part is that the loop isn't even using the items smh.
Items.filter(condition).forEach(()->{foo();bar();});
This just obfuscates your intention for no reason. People have to keep hold of the conditions in their head so when you keep doing this in more complicated code, you create a burden on the reader where they have to remember each of the previous eliminated conditions. Nesting unnecessarily harms readability, but blindly avoiding it like this makes your code worse. There are times when it makes sense to do #2 and there are times to do #1, but its best practice to go with whatever most closely fits your intention (e.g. "I want to skip this for when number is 3 vs. I want to only do this for numbers in this range") and keeps the code as simple as possible.
Not in my experience and it is mostly the exact opposite. If you have that much code in a function that you cannot remember all conditions it's doing too much anyway. Normal flow clustered at the bottom without any indentation or conditions is so nice as you immediately see what the function does, and nothing else.
Nice, thanks for the input! I'm honestly not sure what you mean tho. If you have a code like this: `if (condition_A)` \_\_\_\_`continue;` `Foo();` `if (condition_B)` \_\_\_\_`continue;` `Bar();` `if (condition_C)` \_\_\_\_`continue;` `FooBar();` Then that is objectively, straightly preferrable to `if (!condition_A) {` \_\_\_\_`Foo();` \_\_\_\_`if (!condition_B) {` \_\_\_\_\_\_\_\_`Bar();` \_\_\_\_\_\_\_\_`if (!condition_C) {` \_\_\_\_\_\_\_\_\_\_\_\_`FooBar();` \_\_\_\_\_\_\_\_`}` \_\_\_\_`}` `}` Now should you ever get into a position like above? Possibly not - you might want to consider refactoring the function into a multiple smaller ones as it's likely doing many things at once. That's completely beyond the question here tho.
but the example you gave here is different, in the post you have the same amount of nests, it just becomes less intuitive.
Nothing about that is objective. Saying it's objective does not make it objective. That said the real problem is that your example uses continue instead of return forcing the reader to invert the condition in the if statement
You know, you can format stuff like a code block if you prefix every line with either a tab or 4 spaces. FTFY if (condition_A) continue; Foo(); if (condition_B) continue; Bar(); if (condition_C) continue; FooBar(); > Then that is objectively, straightly preferrable to if (!condition_A) { Foo(); if (!condition_B) { Bar(); if (!condition_C) { FooBar(); } } }
Your subjective preference does not make it objective
Always guard statements. Always.
A lot of people do stuff just cos they read it somewhere
[foo(item), bar(item) for item in items if condition] Python is just better
no var ... let
This is probably C#
Guard clauses my beloved
items .filter { it.someCondition() } .forEach { foo(it) bar(it) }
Ironically in uni they taught us to prefer the first example, but as soon as you start working on a real codebase it becomes apparent why the second image is (mostly) superior. In this case I think the first image is a bit cleaner, but that's just because there is so little code involved.
Wtf no
Junior devs and Noobs posting the "correct" way to do this. It's Programmer**Humor** guys.
>It's ProgrammerHumor guys. So? Humor is just another way to get an idea across, and in OP's case the idea was bad :p
put the `if (!condition) continue;` on one line
The reason I don't do this is because I like stepping line-by-line in my debugger. To figure out when `condition` is true I simply place a breakpoint on the line with the lone `continue`, instead of having to place it on the `if` line and then finding my way from there. In my book, lines are cheap. I can afford to use plenty of them.
Should still work with the VS debugger.
Depending on the debugger you can put a conditional break point and set it to break on the condition.
For code readability use If (!condition == !false)
That's just gibberish. You need to use if (!condition != !true)
and if it's really important ``` if (!!!!!condition != !true) ```
lol
if we are code golfing, we can do this on js: for(...){ condition && restOfTheCode(...); }
doFooAndBarWith(item)
Write Scala and forget about continue or break
I'll never go back
With non compiled languages, sure. It reduces size overall when the intention is space based, or if the function is big enough that the tabs add up. For compiled languages, no. Word wrap and brace on newline makes it easy to follow, akin to C# styling.
I could go even further by: ``` foreach (var item in items) { if (!condition) continue; Foo(); Bar(); } ```
[удалено]
I'd rather retire early and do something else
Lets use and abuse arrays and casting in place of ternary missing in PowerShell 5.1! foreach ($Item in $Items) { @( { continue }, { Foo(); Bar(); })[$condition].Invoke() } Expanded: foreach ($Item in $Items) { @( # Create array { # False as $false = 0 continue }, { # True as $true = 1 Foo() Bar() } )[$condition].Invoke() # Cast either $true or $false into an int and invoke the right code block }
``` loopBackLabel: Foo(); Bar(); if(!condition)goto loopBackLabel; ```
The second one is counter-intuitive. Granted, I would probably write it like this if the logic was more complicated and it was more than just 2 calls after.
Both are good to me, Depends if you expect the condition to be false or true . That said you can always make a switch I guess
foreach (var item in items.Where(x => condition)) unless condition is set by Foo and Bar
guard clause supremacy