Controlling the Flow

Now that you have a core set of variables, it’s time to drive decisions with them. As with any programming language, Crystal has a number of control flow constructs that let you specify the flow of program execution. We’ll go over some of them here, and we’ll add some more info in Chapter 3, Typing Variables and Controlling the Flow.

Making Choices

Crystal offers the classic if expression with variations that let you create complex structures. A simple if expression tests a condition and does something if that condition is met:

 hardness = 7 ​# quartz
 
 if​ hardness < 8
  puts ​"softer than topaz"
 end
 # => softer than topaz

In this case, 7 is less than 8, so Crystal will report “softer than topaz.” If the value of hardness was 8 or greater, the condition would not have been met, and the if expression would have done nothing.

Crystal also supports more intricate if expressions, adding elsif and else statements to test more conditions in a single location. (For code readability reasons, keep your conditions similar within a single if expression.)

 hardness = 5.25
 if​ 0 < hardness < 5
  puts ​"softer than apatite"
 elsif​ hardness < 8
  puts ​"harder than apatite, softer than topaz"
 else
  puts ​"topaz or harder!"
 end
 # => harder than apatite, softer than topaz

In this case, the first conditional check, on the initial if, failed. The second check, on the elseif, succeeded. The last check, the else, which gets used if none of the other conditions matched, never gets called.

When Crystal evaluates conditions, it calculates whether they are truthy or falsy, not strictly true or false. Crystal takes a fairly constrained view of falsy, treating false and nil (and null pointers) as falsy, and everything else—true, numbers, zero, strings, arrays, you name it—as truthy.

Each branch of an if expression returns a value, so you can combine variable assignment with if to create precise choices.

 output = ​if​ 0 < hardness < 5
 "softer than apatite"
 elsif​ hardness < 8
 "harder than apatite, softer than topaz"
 else
 "topaz or harder!"
 end
 output ​# => harder than apatite, softer than topaz

You can also write the if statement as a suffix, coming at the end of a variable assignment.

 output = ​"softer than topaz"​ ​if​ hardness < 8 ​# => softer than topaz

Depending on the condition you’re testing, it may be more readable to use unless here:

 output = ​"softer than topaz"​ ​unless​ hardness >= 8
 output ​# => softer than topaz

While if expressions are readable for simple conditions, they can grow unwieldy quickly. If you want to test the same value against different conditions, the case-when expression can create code that is easier to read.

 output = ​case​ hardness
 when​ 4
 "hard as fluorite"
 when​ 7
 "hard as quartz"
 when​ 10
 "hard as diamond"
 else
 "can't say how hard"
 end​ ​# => "can't say how hard"

You can also create a case-when expression that uses conditionals much like an if statement.

 output = ​case
 when​ 0 < hardness < 5
 "softer than apatite"
 when​ hardness < 8
 "harder than apatite, softer than topaz"
 else
 "topaz or harder!"
 end​ ​# => harder than apatite, softer than topaz"

Looping Around

Sometimes you want to run a block of code a fixed number of times. You can use the times method of the Int type, or you can structure the code block with do ... end if it spreads over multiple lines, or use {...} instead.

 # Int#times
 5.​times​ ​do
  p ​"Hi"
  p ​"Low"
 end
 # same as:
 5.​times​ { p ​"Hi"​; p ​"Low"​ }

This is simple but speedy: times is as fast as a C loop because it’s inlined in the executable code.

Sometimes you want not just to run code a certain number of times but to cover a range of values. You can use Crystal’s Range type. You declare ranges with syntax like 2..7, or in general, start..end. The result contains all items from start to end inclusive. (You can use ... for an exclusive range, leaving out the end value.) The start and end values can be integers, characters, or even strings. If you want to inspect the values of a range, you need to convert them to arrays with to_a.

 inc = 2..7
 p inc.​to_a​ ​#=> [2, 3, 4, 5, 6, 7]
 exc = 2...7
 p exc.​to_a​ ​#=> [2, 3, 4, 5, 6]

Ranges have an each method, which lets you do something with each of the items in succession. This can be useful, for example, to extract a set of values from an array.

 # Range#each
 mohs_list = [​"nothing"​, ​"talc"​, ​"gypsum"​, ​"calcite"​, ​"fluorite"​, ​"apatite"​,
 "orthoclase feldspar"​, ​"quartz"​, ​"topaz"​, ​"corundum"​, ​"diamond"​]
 (2..5).​each​ ​do​ |i|
  puts mohs_list[i]
 end
 # produces:
 # gypsum
 # calcite
 # fluorite
 # apatite

If you want to process every value in an array, you can skip the range and just use the array’s own each method.

 # Range#each
 mohs_list = [​"nothing"​, ​"talc"​, ​"gypsum"​, ​"calcite"​, ​"fluorite"​, ​"apatite"​,
 "orthoclase feldspar"​, ​"quartz"​, ​"topaz"​, ​"corundum"​, ​"diamond"​]
 mohs_list.​each​ ​do​ |mineral|
  puts mineral
 end
 # produces:
 # nothing
 # talc
 # gypsum
 # calcite
 # fluorite
 # apatite
 # orthoclase feldspar
 # quartz
 # topaz
 # corundum
 # diamond

If you want to create your own logic for loops, Crystal offers a very basic loop do ... end. This starts an infinite loop, so you have to exit from it with an explicit break:

 n = 1
 loop​ ​do
  puts ​"a mighty crystal"
  n += 1
 break​ ​if​ n == 3
 end
 # => a mighty crystal
 # => a mighty crystal

Crystal also implements the typical while construct, which loops until its condition becomes false:

 a = 1
 while​ (a += 1) < 10
 if​ a == 3
 next
 elsif​ a > 6
 break
 end
  puts a
 end​ ​# => 2, 4, 5 and 6 on successive lines

Based on a condition, next goes to the next iteration in the loop, while break exits from the loop. Also, if you’re testing that a condition is not true, you can make your code easier to read by writing until condition in place of while !condition.

Empty Strings

images/aside-icons/tip.png

Crystal’s conventions for character and string literals are more like C than like Ruby, which doesn’t have the explicit Chars type for individual characters. Keep in mind that the empty char literal doesn’t exist in Crystal: if you need an empty string, use "".

Your Turn 4

Write a Crystal program using while and if that correctly puts out the text of this song.[26] As a bonus, try it with the method Int32#downto.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.129.90