Ruby Loops
Tags: ruby, tutorial, beginners
Published: 2020-05-20 21:49:40

Today we're going to go over the concept of looping in Ruby. **Looping** is an action that tells our program to do the same thing over and over again with a few lines, rather than manually typing out our desired loop length. 

<h1>Why Loop?</h1>
Simple answer: because as programmers we aspire to be as lazy as possible.

Think a minute about writing a program that requires you to perform an action 5 times, then imagine having to do it 100 times. What problems could you potentially run into typing all occurrences manually? First is a matter of convenience, even if you copy/paste to ensure all the lines are ***exactly*** the same, that's still a tedious manual task that takes away time from improving your program elsewhere. Next, what if you lose count after say 47? What if you accidentally type it in only 97 times? 103 times? This could potentially cause your program to not work as intended in other parts of your code as well.

**Loops** automate this process for us with only a couple lines of code in Ruby. A program that would require a `String` to be repeated 100x would use 100+ lines of code if manually looped. You can perform the same functionality in Ruby with about 3 lines of code:

```ruby
100.times do
  puts "Here we go 'round!"
end
```
And just like that, a seemingly daunting task is complete in a matter of seconds!

Ruby has several different types of loops, some with overlapping functionality and some meant to be more niche in use. We'll go over some of the more common types of loops and explain them in greater detail below.

<h1>Times Loops</h1>
Let's start by breaking down the above loop, and highlight the keys that make this function:
```ruby
100.times do
  puts "Here we go 'round!"
end
``` 
1. `100.times` is the number of times we want our program to perform the following action. The number in front of `times` also serves as a built-in `break` of the loop, where some other loop types require you to add it in elsewhere. 
2. the `do`/`end` creates a block that tells our program what action we want to perform. In this case `puts` "Here we go 'round!" The `do`/`end` block will be a common participant in a number of Ruby lessons. 

The beauty of Ruby is it's designed with being programmer-friendly as it's #1 priority. `times` is one of the best examples of that in the entire language.  

<h1>The "Loop" Keyword</h1>
Next up is the `loop` keyword. `loop` is the simplest construct we have for looping in Ruby. It will just execute a block (the action(s) between `do`/`end`) over and over until it is told to stop.

 Let's built a similar method as above from scratch:

```ruby
loop do
  puts "Here we go 'round!"
end
```
This will put the string "Here we go 'round!" into our console in an eternal loop until we stop it with `ctrl`+`c`. Not terribly useful overall in its current form if you ask me. Let's add a `break` command to stop the loop on its own:

```ruby
loop do
  puts "Here we go 'round!"
break
end

# returns =>
#Here we go 'round!
```
So now we have a "loop" that only executes our desired command once. The thing is `loop` is so simple, that it requires a bit more effort to work at the same level as `times`, which kind of defeats the point as Rubyists. 

If we wanted to write a `loop` command equivalent to our `times` command above, we would have to add a counter to keep track of when to break the loop like such:

```ruby
counter = 0
loop do
    counter += 1
    puts "Here we go 'round!"
    if counter >= 100
        break
    end
end
```
That is almost triple the number of lines it took our `times` loop to accomplish the same task. 

**Note** You may be wondering what the `+=` is doing. It basically performs the same function as the line `counter = counter + 1` which increments and keeps track of the updated value of the `counter` variable. It simply condenses the code. 

Since we have a `counter` variable, I'll take this opportunity to demonstrate how to keep track of variables inside loops. Stop me if this looks familiar to you:

```ruby
counter = 0
loop do
    counter += 1
    puts "Here we go 'round #{counter} times!"
    if counter >= 100
        break
    end
end
#returns =>
#Here we go 'round 1 times!
#Here we go 'round 2 times!
#...
#Here we go 'round 99 times!
#Here we go 'round 100 times!
```
Yup, we can interpolate with `#{variable}` inside loops to display and even manipulate the current values of variables.

<h1>While Loops</h1>
`while` loops provide a unique approach to a similar end. Basically `while` translates the given condition to a `boolean`, and if that condition is `true` the `while` loop will then execute until the condition is `false`. Like `times` `while` is it's own built-in `break`, so there is no need to declare a direct `break` inside the action like with `loop`. Writing a loop like above in this style would look like this:

```ruby
counter = 0
while counter < 100
  puts "Here we go 'round!"
counter += 1
end
```
Removing the `break` command saves us some space, but in a strict counting situation `while` still isn't as advantageous as the `times` loop. However, in slightly more complex programs, `while` has some strong advantages over `times`. For example, if we had an adventurer who was poisoned and losing health, we would want to keep track of our HP so we know when to use our auto-revive potions, right? And we would definitely need a reminder to pop up when we died from the poison too, right?  

```ruby
hp = 100
status = "poisoned"
potions = 2

while status == "poisoned" && hp >= 1
    hp -= 1
    puts "HP: #{hp}"
    if hp == 0 && potions >= 1
        puts "You've used an auto-revive potion!"
        potions -= 1
        hp += 100
        status = "normal"
        puts "HP: #{hp}"
    elsif hp == 0 && potions == 0
        puts "The poison was too much to bear!"
    end
end
```
This code is a bit busy, but doesn't contain anything we haven't covered already! The gist is we set initial values for `hp`, `status`. & `potions` and manipulated them throughout the `while` loop. Using `times` in this situation would require multiple loops and more lines for what would likely be a less desirable result. Also note the `do` in `while` is not necessary

On a more personal note, I'm glad we had enough potions to survive being poisoned!

<h1>Until Loops</h1>
`unitl` is basically the *inverse* of `while`. Where a `while` loop executes when something is `true`, an `unitl` loop will execute *until* something becomes true. For example:
```ruby
counter = 0
until counter == 20
  puts "Almost 20!"
  counter += 1
end
```

Just like `while` the `do` is optional.

While you can do complex code with `until`, in my opinion, the best time to use `until` is as a statement modifier. Example:

```ruby
i = 0
puts i += 1 until i == 5

#returns =>
#1
#2
#3
#4
#5
``` 
This is a very simple example, but it does an effective job of manipulating the value of a variable to the desired number in only 1 line of easy to read code.

<h1>Concluison</h1>
The key to loops in Ruby, and any programming language really, is knowing what loop is best used in a given situation. Do you want to repeat an action a specific amount of times with little additional context? Use `times`!. Want to drown someone special in your eternal love? Use `loop` (with no `break`!)! Wanna get a little more complex with your code? Use `while`! Need a simple and precise statement modifier? Use `until`!

<h1>Loops Lab</h1>
Now that you've learned about `Loops`, let's head on over to the [Ruby Loops Lab](https://github.com/BMWeygant/Ruby-Tutorial-Loops-Lab) and apply those lessons by completing the assigned tasks.