Programming with ooc/Control

From Wikibooks, open books for an open world
< Programming with ooc
Jump to: navigation, search
This computer programming article is available for pseudocode, Ada, and ooc.

Conditionals[edit]

Conditional clauses are blocks of code that will only execute if a particular expression (the condition) is true.

if-else[edit]

The if-else statement is the simplest of the conditional statements. They are also called branches, as when the program arrives at an if statement during its execution, control will "branch" off into one of two or more "directions". An if-else statement is generally in the following form:


if (condition) {
    statement
} else {
    other statement
}

If the original condition is met, then all the code within the first statement is executed. The optional else section specifies an alternative statement that will be executed if the condition is false. Exact syntax will vary between programming languages, but the majority of programming languages (especially procedural and structured languages) will have some form of if-else conditional statement built-in. The if-else statement can usually be extended to the following form:


if (condition) {
    statement
} else if (condition) {
    other statement
} else if (condition) {
    other statement
...
} else {
    another statement
}

Optimizing hints[edit]

When this program executes, the computer will check all conditions in order until one of them matches its concept of truth. As soon as this occurs, the program will execute the statement immediately following the condition and continue on, without checking any other condition for truth. For this reason, when you are trying to optimize a program, it is a good idea to sort your if-else conditions in descending probability. This will ensure that in the most common scenarios, the computer has to do less work, as it will most likely only have to check one or two "branches" before it finds the statement which it should execute. However, when writing programs for the first time, try not to think about this too much lest you find yourself undertaking premature optimization.

Having said all that, you should be aware that an optimizing compiler might rearrange your if statement at will when the statement in question is free from side effects. Among other techniques optimizing compilers might even apply jump tables and binary searches.


match[edit]

Often it is necessary to compare one specific variable against several constant expressions. For this kind of conditional expression the match statement exists. For example:

match (x) {
  case 1 =>
     walkTheDog()

  case 5 =>
     launchNuke()
 
  case =>
     selfDestruct()
 }

The last case here has an empty condition. It's called a catch-all case, which code will be executed in case none of the above cases are matched.

Unlike switch in C or Java, there is no need to break of a case explicitly with the break keyword. Only one case per match is only ever executed. This enforces simple designs, at the cost of some flexibility. For example, Duff's device can't be written in ooc.

However, in some ways, match is more flexible than a switch. For example, you can have a match without an initial condition:

match {
  case (x < 0) =>
     -1

  case (x > 0) =>
     1
 
  case =>
     0
}

Here we define custom conditions inside the cases, rather than values to compare. The parenthesis are optional, but included here for the sake of readability. It behaves exactly as if we had written match (true) { on the first line.

Another difference with traditional switch statements is that a match can be used as an expression. In that case, it's evaluated to the value of the last statement in the case executed. It's a good idea to have a catch-all case when you use a match as an expression, so that there are no cases where the value is undetermined.

Finally, values are compared using the equality operator ==, which can — like any ooc operator — be overloaded. This means that match works with strings as well, which have == overloaded in the core sdk:

match (humor) {
  case "sad" =>
     ohNoes()

  case "happy" =>
     yay()
 
  case =>
     bahHumbug()
}

By overloading the == operator, it is possible to use any user-defined type in a match statement.

Unconditionals[edit]

return[edit]

return exits the current function, optionally passing to the outer scope any value you gave to it

 return 42

return doesn't always have a value, it can also only simply be used to exit a function at any point

 return

goto[edit]

ooc doesn't have a goto keyword. You can control the flow of loops with break and continue, and exit function via return. The ooc language thrives to be simple, and considers that goto doesn't belong to a high-level language. See Edsger Dijkstra's work for further reading [1].

Loops[edit]

Loops allow you to have a set of statements repeated over and over again.

Endless Loop[edit]

The endless loop is a loop which never ends and the statements inside are repeated forever. Never is meant as a relative term here — if the computer is switched off then even endless loops will end very abruptly.


while (true) {
  doSomething()
}

Loop with condition at the beginning[edit]

This loop has a condition at the beginning. The statements are repeated as long as the condition is met. If the condition is not met at the very beginning then the statements inside the loop are never executed.


while (x < 5) {
  x = calculateSomething()
}

Loop with condition in the middle[edit]

Sometimes you need to first make a calculation and exit the loop when a certain criterion is met. However when the criterion is not met there is something else to be done. Hence you need a loop where the exit condition is in the middle.


while (true) {
  x = calculateSomething()

  if (x > 5) {
    break
  }

  doSomething(x)
}

Skipping iterations[edit]

The continue keyword can be used to zap the rest of the loop's body and go directly to the next iteration.

while (true) {
  x = calculateSomething()

  if (x == 5) {
    // skip the 5th step
    continue
  }

  doSomething(x)
}

for loop[edit]

Quite often one needs a loop where a specific variable is counted from a given start value up or down to a specific end value. You could use the while loop here — but since this is a very common loop there is an easier syntax available.


for (i in 1..11) {
   doSomething(i)
}

Ranges are delimited by .. — You can store ranges and re-use them later, e.g.:

range := 1..11
// then, later:
for (i in range) {
   doSomething(i)
}

This works because 1..11 constructs a new Range, which is a cover type. Covers are explained in later chapters.

for allows to iterate through values of any types, as long as they implement the iterator method, returning an Iterator. For example, you can iterate over ArrayLists:

list := ["moody", "jolly", "bored"] as ArrayList<Int>
for (state in list) {
  println(state)
}

See also[edit]

Wikibook[edit]

References[edit]

  1. Edsger Dijkstra (March 1968). "Go To Statement Considered Harmful" (PDF). Communications of the ACM 11 (3): 147–148. doi:10.1145/362929.362947. http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF. "The unbridled use of the go to statement has as an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress. ... The go to statement as it stands is just too primitive, it is too much an invitation to make a mess of one's program.".