Early returns

Hi there.
I personally prefer to use if, else and else if instead of using only if and the “return” statement.
I used “return” before but only in an final “else” statement, just to be sure that i have every possible result covered. By example:

if (a>b) { do this }
else if (a<b) { do this }
else { return } .

In Bull Cows Game, I created a CheckLives() function, where I decrement the Lives every time I call it and the I check the Lives amount and, if necessary, I call the EndGame() function.

I personally find it more readable this way. What do you think? :slight_smile:

1 Like

Looking good!

Personally, I prefer the if-return statements. Makes the code much more readable. If you have 20 nested if-else statements, it will be a nightmare to read and debug.

Still very new to programming, but think that switch statement is also good to use if you have many if-else nested, code is more readable, think it’s faster too.

It is surely a preference thing. 15 years ago when I started coding, I learned Borlad Pascal.
I can’t remember if Pascal had return statements :slight_smile: so I prefer the nested if- else if - else statements.
In my opinion, if I have 10 if - else if statements is just the same as having 10 if-return statements. The compiler checks every if-else if statement and if-return statement until every condition is met.
When the statement has all valid conditions, he (the compiler) executes the code in the valid if statement and ignores any other if or else statement. (regardless if its an if - else if - else statement or an if - return statement).

But as I said, it’s more a preference. End result is the same, and compiling times should be the same. :slight_smile:

Here is my final code for the ProcessGuess function.

1 Like

Yeah,

It’s just a code style. I always did if-else if-else, but after this lecture, I switched to if-return, just prefer it more. For example, if I look into your code, I see:

(IsIsogram(Guess)) and (!IsIsogram(Guess)),

where as I prefer to type:

(IsIsogram(Guess) == true) and (IsIsogram(Guess) == false)

They do the same thing, but to me, it’s more readable. Can’t tell you how many times I have failed to see “!” in code. Good job tho! :slight_smile:

Edit*: Noticed your last “else” is completely useless. You covered all bases with 4 else-if statements. You should either remove 1 else-if (not isogram and not N char long) or just put something else in “else” statement, but it will never execute.

Hi :slight_smile:
Sorry for the late response.
Yeah, it always comes up to the programmer and how his code is compiling.
As for my last else, I know that it will never execute, but apparently this is how I learned it over 15 years ago: always end an if - else if statement with an else that will cover any other possible outcome that you never think of.
It is just an “safe guard” that should not execute if I covered all possible outcomes.
But if I overseen something, I lie to know that I have a line that will catch that possible outcome that I overseen.
Cheers :slight_smile:

Have to say I was surprised to see this here, always felt to me like the slightly more acceptable cousin of goToLine xx. It’s just too open to abuse and unclean code practices especially during maintenance work.

if(something){
    somthingElse = calc;
    if(something + somethingElse){
        doSomething;
        return;
    }else{
    }
    doSomethingElse;
}else{
}

I’d expect a refactor of multiple inline if else’s to look more like

void UBullCowCartridge::ProcessGuess(FString Guess){
    
    bool bCorrectAnswer = HiddenWord == Guess;
    bool bIncorrectLength = Guess.Len()!=HiddenWord.Len();
    bool bNotAnIsogram = false; //TODO: compute isAnIsogarm()
    bool bAllowableError = bIncorrectLength || bNotAnIsogram;
    
    if( bCorrectAnswer){
    
        PrintLine(TEXT("You win"));
        GameOver();
   
    }else if(bAllowableError){
        
        HandleAllowableError(bIncorrectLength, bNotAnIsogram);
        
    }else{
        
        //TODO: bulls and cows check
   
        PrintLine(TEXT("You have %i guesses left"), PlayerLives,);
        if(PlayerLives == 0) GameOver();

    }
}

void UBullCowCartridge::HandleAllowableError(bool bIncorrectLength, bool bNotAnIsogram){
    
    if(bIncorreectLength) PrintLine(FString::Printf(TEXT("The hidden word is %i characters long."), HiddenWord.Len()));
    
    if(bNotAnIsogram) PrintLine(TEXT("The hidden word is an isogram, no repeating characters."));
    
    PrintLine(TEXT("Please try again ..."));
}

If I’d asked for a refactor to make it more maintainable and concise on a PR because of this and I got a load of if return statements instead the following conversation would be less than comfortable for the dev unless they were straight out of uni.

1 Like

Hi Jim,
I am sure that my code is not optimized or refactored properly.
You clearly have more experience in coding than most of us, but you must consider that for the purpose of this exercise, for the level of this exercise, and for the experience of the people here, your refactored code is a little to advanced.
You are using pointers, multiple assign, using “?” conditional operator in a single line assignment.
As for readability, I find yours, although shorter, harder to read.
The purpose of this part of the course is to teach the basics of C++ in Unreal and to optimize a code with the knowledge you accumulated to that point.
You can’t really compare the level of coding in this course with some real world development.
Cheers.

Hey, yeah forgot I had the ternary stuff there so removed it so not to confuse anyone. The point was using early boolean assignments with descriptive naming and breaking out common logic like the error/validation stuff allows the reduction of the masses of if elses without the introduction of multiple exit points to the function.

In hind sight game code is much shorter lived than the type of code base I normally work with and less likely to have gone through incremental update so it’s probably far less of a problem than it feels to me.

Honestly if the quiz hadn’t tried to make me tick a box saying what a good idea it was and you hadn’t mentioned using if else instead I probably wouldn’t have posted. It’s just I see those 5 if statements with their own returns and in my head it ages 3-5 years and every other return has disappeared and the blocks now 3 layers deep with 5 conditions on each branch and my blood runs cold :stuck_out_tongue:

Hi Jim,
OK, now your code is more readable for this level of implementation.

Yeah, I get your point about using this early returns, this is why my “refactoring” excluded the possibility of using this “forced” exits. :stuck_out_tongue: (it’s a simple if-else-if nested statement, not some switch statement that needs a break for exiting after each case - maybe not a good example but perhaps you get my point).

Nevertheless, it’s clear that you are way more experienced that most of us and I really like that you edited your initial post and re-refactored your refactored (:slight_smile:) code so that even the unexperienced are able to read and even learn form it. I applaud you for that! :clap:

Way to go and good job. Cheers :beers:

Thanks to everyone who contributed to this thread! It contains really helpful discussion on precisely the topic I was going to ask about!

Having done the Unity course from gamedev.tv, which uses if-else if, I was curious as to what best/better practice was. And having tried my own first ‘no training wheels’ game, I occasionally got lost in nested logical operators.

I wonder if there’s a general principle that can be abstracted from the examples in this thread? Something like:
Use if-else if when you have a complete list of mutually exclusive outcomes and you want to provide for no other outcomes (e.g., some integer variable takes a value 1, 2, or 3 only)
Use if-return when your list of things to evaluate is not mutually exclusive, or otherwise don’t want to attempt a grand final solution (e.g., you know what you want for 1, and aren’t fussed but 2 or 3)
Something like that? Or am I way off? (for example - why have conditions that you don’t know what to do with?!)

Probably the principle I’m looking for is in a CompSci textbook somewhere, but I didn’t do CS at university.

Ignorantly yours,
AJ

Hi AJ,
honestly, I do not think there’s a right or wrong way to do it or a best practice. As long as the end result is as expected. I mean, there are some basic rules that you must, let’s say, obey (programming language specifics) but as far as your programming style, that is up to you. If you work alone, do it your way. If you work in a team, you should try and adopt some coding style from your team members and the team members from you.

Here are some examples from a project I was working on:

void AMainCharacter::AttackButtonPressedDown()
{
	bAttackButtonPressedDown = true;

	if (MovementStatus == EMovementStatus::EMS_Dead)
	{
		return;
	}

	if (!ActiveOverlapingItem)
	{
		if (EquippedWeapon)
		{
			Attack();
		}
	}
}
  • In this case, I use the return statement because if the players movement state is “dead”, the rest of this function does not matter anymore.

The following code uses if-else if-return statement:

void AMainCharacter::Jump()
{
	UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
	if (MovementStatus != EMovementStatus::EMS_Dead)
	{
		//Super::Jump();
		if (AnimInstance && CombatMontage && EquippedWeapon)
		{
			AnimInstance->Montage_Play(CombatMontage, 2.0f);
			AnimInstance->Montage_JumpToSection(FName("JumpEquiped"));
		}else if (AnimInstance && CombatMontage && !EquippedWeapon)
		{
			AnimInstance->Montage_Play(CombatMontage, 2.0f);
			AnimInstance->Montage_JumpToSection(FName("JumpUnequiped"));
		} else
		{
			return; //Safety net, it will never trigger
		}
	}
}
  • In this particular case, I do use the if - else if statement, and in the end I use an “catch statement” else return. Why I call it an catch statement? I find it important in an nested if-else if to use an final else statement to catch every other possible logical expression that you overseen. Like a safety net.

You can use it even for debugging: my last else could look like this:

} else
		{
			UE_LOG(LogTemp, Warning, TEXT("!!!SOMETHING IS WRONG IN YOUR JUMP CHECK!!!"));
		}
	}
}

Maybe you will use it, maybe not. But it’s not bad to have it.

Both of this examples are in the same project and the same .cpp file.

I know that this does not really answer your question, but I think it’s more of practice than of the wright way.
Two different programmers will have 2 different opinions on best practices.
I do not remember any rule that “forbids” or “recommends” using nested if’s or a bunch of if-return statements.

That is the beauty of programming: if a + b = 1 it does not really matter if

  • a = sqrt(4) and b = -1 => sqrt(4) + (-1) = 2 + (-1) = 2 -1 = 1, OR
  • a = 1 and b = 0 => 1 + 0 = 1
    The end result is the same a + b = 1

But like [C0d3M0nk3y] said in an above post: “If I’d asked for a refactor to make it more maintainable and concise on a PR because of this and I got a load of if return statements instead the following conversation would be less than comfortable for the dev unless they were straight out of uni.”

Thanks @z.goerbe!
Reflecting on your words, I wonder if the key difference is whether there’s a need for a ‘catch all’ statement. If so, if-elseif-else is key.
And I take @C0d3M0nk3y’s points too. Long lists of if-returns seem a bit untidy. Though I can’t help wonder as well: given an equally long list of conditions, why is if-elseif statements any more tidy or concise than a long list of if-returns? Is it merely the way that if-elseif-else define a limited universe of outcomes with a catch-all, whereas if-returns seem more open-ended?

I get pretty crazy with my code, so I very much prefer to make the trees as flat and DRY as possible.

Coming from PHP though, I’m used to just abusing array functions.

Privacy & Terms