Programming Tips #1 - If Statements
Programming Tips #2 - Making Progress
Programming Tips #3 - Design
Programming Tips #4 - Datums
Programming Tips #5 - Organization
Programming Tips #6 - Procs & Organizing Code
Programming Tips #7 - Comments and Whitespace
When you first start programming, the problem you're trying to solve is "how do I write code that'll do ______?". Initially you're happy just to get something working, who cares what the code looks like. But when you try to develop a complete game this indifference can become a problem. As your code gets messier and messier, the project becomes harder to work on - you're more likely to write code that has bugs and they become harder to track down and fix. Eventually it'll get to the point where you're wasting a lot of time, don't feel productive, and are more likely to stop working on the project.
As a more experienced programmer, instead of asking "how do I do ______?", you should start asking "what's the best way to do ______?". In this post we'll look at how using if statements and loops certain ways can improve the readability of your code.
There are many ways to write a proc that does a certain task. This means that there are many uses for the same basic DM language structures - variables, procs, and loops can all be used for many different things. This creates a problem. When you're reading code you have to figure out how each part is being used.
We make this easier by only using certain tools for certain tasks. For example, for loops and while loops are interchangeable - any for loop can be written as a while loop and any while loop can be written as a for loop. Even though they're essentially the same, we use them for different tasks to make the code easier to understand.
This lets you make assumptions when you're reading code so you can easily look at a for loop or if statement and understand what's going on.
Loops
A for loop is used to iterate over a bounded set:
for(var/i = 1 to 7)
for(var/mob/m in view())
for(var/area/a in world)
The for loop itself clearly defines the set of values you're looping through. We could use a while loop or recursion to do the same thing but the set of values wouldn't be as clear:
// first way:
for(var/i = 1 to 7)
world << i
// second way:
var/i = i
while(i <= 7)
world << i
i += 1
// third way:
proc
do_something(i)
if(i <= 7)
world << i
do_something(i + 1)
do_something(1)
These all do the same thing but the first method is the most clear.
A while loop is used to loop while a condition is true:
var/name = ""
while(!name)
name = input("Please enter your name:")
The loop continues as long as no name has been entered. In these types of loops we don't care about a set of values or how many times the loop runs, we care about the condition that keeps the loop going. A while loop makes the condition clear, so we wouldn't use a for loop in this case.
This is why the goto statement isn't used. You can use the goto statement to create a loop, but the kinds of loops you create will almost always be a type that's more clearly represented by using a for or while loop.
If Statements
An if statement checks a single condition. This isn't a problem I often see, but sometimes people use loops instead of if statements.
// some people might do this:
for(var/mob/m in oview(5,src))
attack(m)
break
// instead of this:
var/mob/m = locate(/mob) in oview(5, src)
if(m)
attack(m)
Using a loop in this case is bad because you're not really using a loop. You're just using the loop as an if statement - the loop's body will execute either zero times or one time, just like the body of an if statement. The reason it's bad to use a loop in this case is because whoever reads the code will see a loop and expect certain things to be true. Because you're not really using it as a loop, none of those assumptions will be true and the code will take more time to understand.
Another thing to keep in mind about if statements is whether you use the if statements body to handle the nominal case and use the else clause to handle the exception or the other way around. For example:
// first way:
if(valid)
// do something
else
// handle the error
// second way:
if(!valid)
// handle the error
else
// do something
Both methods do the same thing. The first way is better because people expect the if statement to check for the desired condition and its body will handle the nominal case. It's expected that the else clause handles the unexpected or undesired case. Both examples do the same thing but the first way makes more sense. You can avoid this by not using else clauses:
var/result = do_something()
if(isnull(result))
// handle this kind of error
return
if(result < 10)
// handle this other kind of error
return
// handle the case where the result is valid
The if statements are used to check for bad conditions. If all checks pass, the result is known to be good and the processing occurs.
My first programming tips article covers some other concerns about if statements - particularly how to write the conditions in clearer ways.
This is a simple topic but it's one of the first things that people stumble on when transitioning from "newbie" to "intermediate". A newbie is just happy to write code that works. An intermediate programmer isn't just concerned with getting code working, they're concerned with how it works and how it's organized. If you have the mindset "the code works, who cares what it looks like?" you're going to be stuck in the newbie stage. To work on larger, more complex games and be able to see them through to completion you'll have to be concerned with how your code is organized.