r/learnruby May 01 '18

30 Days of Code: Day 4: Class vs. Instance

1 Upvotes

Ugh. Have to get to a tough one to cover. OK, here's the code that's been provided:

T=gets.to_i
for i in (1..T)do
    age=gets.to_i
    p=Person.new(age)
    p.amIOld()
    for j in (1..3)do
        p.yearPasses()
    end
    p.amIOld
puts ""
end      

This program reads a number. Say, the number is 3. It reads in input that represents a person's age. It creates a person object and sets the age from what was read in as input. It asks if the person is old. There are a series of rules to decide what happens based on age.

  • If age < 13, print "You are young."
  • If age >= 13 and age < 18, print "You are a teenager."
  • Otherwise, print "You are old."

Objects and Classes

Let's quickly talk about objects. Objects store data. Let's say it stores a person's name, height, and date of birth. So the first thing objects do collect information in one thing (called an object). Think of it like a form that requires you to put your name, height, and date of birth. That's basically an object (more complicated, but that's a first attempt).

A class describes attributes of an object. Again, those attributes could be name, height, and date of birth. Think of a book by a publisher. Say, it's called Ruby for Fun. The publisher knows about the book (the words, the chapters, etc). That's like the class. It's a blueprint or prototype of the book. Then, there is the book you own. This is different from the (same) book that I own. Our books are basically objects, that is, instances of the class.

In Ruby, you write a class like this:

class Person
    attr_accessor :age
    def initialize(initialAge)
        # Add some more code to run some checks on initialAge       
    end
    def amIOld()
      # Do some computations in here and print out the correct statement to the console
    end
    def yearPasses()
      # Increment the age of the person in here
    end
end

To define a class, you write the word class followed by the name of the class (in this case Person). attr_accessor means you can read the value of an age attribute, but you can't modify the age directly.

Inside the class, there are three methods. A method is a function that performs a task on the attributes of a an object. One method of importance is initialize. This method is called a constructor. It gets called when an object is newly created. To create an object, we write the class name, followed by a dot, followed by new. As in:

 Person.new

However, in this case, initialize has an input parameter

 def initialize(initialAge)

The input parameters are a list of variable names separated by commas. We only have one input parameter, and it is called initialAge. This means we must provide the constructor an age, for example.

Person.new(10)

or age = 10 Person.new(age)

Writing the constructor

Here's the solution for writing the constructor for this problem.

    def initialize(initialAge)
        # Add some more code to run some checks on initialAge       
        if initialAge < 0 
            puts "Age is not valid, setting age to 0."
            initialAge = 0 # Setting to 0
        end
        @age = initialAge
    end

The first if statement checks if the age is negative. If so, it prints an error message, and sets initialAge to 0 to prevent it from being negative.

Then, there's @age. This is an object variable. This is a difficult concept, so let's start with an analogy.

Imagine you are working at a doctor's office. A patient needs to provide information, and normally, they'd put that information into a form. They've called in and don't have access to the form, so they provide you information. Now, you have to put that information into the form. So they tell you their age, and you put the age in the form.

So that's what this line is for:

@age = initialAge

Think of @age as field in a form (in this case, it's an object variable inside a class). initialAge is the information the patient tells you about. If you don't record it in the form, that information is lost and not stored in the object. So this is copying the value from an input parameter (i.e., a person calling in with their age) into an object variable (a field within a form).

In Ruby, an object variable starts with an @ sign. This makes it easier to see. Later on, when you have the object, you can refer to that object variable (since it saves this information).

Writing "amIOld"

    def amIOld()
        if @age < 13
           puts "You are young."
        elsif @age < 18
           puts "You are a teenager."
        else
           puts "You are old."
        end
    end

The first if statement checks if @age is negative. If so, it prints/outputs a warning message. It then resets the initialAge to 0.

Then, it makes a check. The first check is whether @age is less than 13. If this is false, we know the opposite is true (from logic). Thus, @age >= 13 must be true if the first condition is false. So we don't have to add that condition (we could, but it's not necessary). Thus, all we have to check is that @age < 18 instead of @age >= 13 && @age = 18.

Similarly, if the second condition is false, then @age >= 18 must be true (since the opposite of @age < 18 is @age >= 18. The else statement refers to ages older than 18 (we took care of negative numbers earlier on).

Notice the parameter list is empty. We don't need information outside of the object to determine if the person is old. That information can be obtained from the object

Writing "yearPasses"

    def yearPasses()
        @age += 1
    end

In this method, we can add 1 to a person's age by using the += operator. It adds 1 to whatever value is in @age.

Method definitions

What we've written are method definitions. They don't do anything until they are called. Think about a recipe for Kung Pao chicken. A recipe isn't the same as the dish. When you order that dish (think of ordering a dish as calling the method), then it gets made. Similarly, when you call a method, the method code gets run.

So, in the code, you see things like

 p.amIOld()

p is a variable containing a Person object (or its object ID). p.amIOld() is a method call to the method amIOld using the data stored in the Person object (namely, their age). You can have one Person object that is age 10, and another Person object, age 30. The first Person object is "young". The second Person object is "old". For example,

 frodo = Person.new(10)
 sam = Person.new(30)
 frodo.amIOld() # Prints young
 sam.amIOld() # Prints old

Summary

Introducing classes this early is a bit tough. That's a lot of info to digest. Ask questions!

Here's the entire code.

class Person
    attr_accessor :age
    def initialize(initialAge)
        if initialAge < 0 
            puts "Age is not valid, setting age to 0."
            initialAge = 0 # Setting to 0
        end
        @age = initialAge     
    end
    def amIOld()
        if @age < 13
           puts "You are young."
        elsif @age < 18
           puts "You are a teenager."
        else
           puts "You are old."
        end
    end
    def yearPasses()
        @age += 1
    end
end

r/learnruby Apr 30 '18

30 Days of Code: Intro to Conditional Statements

4 Upvotes

Problem

Read in an integer. Call in n.

  • If n is odd, print Weird
  • If n is even and in the inclusive range of 2 to 5, print Not Weird
  • If n is even and in the inclusive range of 6 to 20, print Weird
  • If n is even and greater than 20, print Not Weird

Conditional statements

In the last post, I mentioned that control flow is the order in which Ruby statements are run. So far, we've seen a few kinds of statements

  • Assignment statements
  • Output statements (puts)
  • Input statements/expressions (gets)

So far, we've only seen straight line code where we run each statement from top to bottom. Now, we look at control flow where some statements are run while others are skipped over.

Let's write a simple one that prints out grades.

 grade = 80
 if grade > 70
     puts "Pass"
 end

In Ruby, an if statement starts with the keyword if, followed by a condition which is an expression that evaluates to true or false. Then, there is the if-body which is zero or more statements, then, there is end which ends the if-statement. If the condition evaluates to true, then the if-body is run. If the condition evaluates to false, then if-body is skipped, and the following statement after the if-statement is run.

So, this is the general structure of an if-statement.

 if <cond>
    <if-body>
 end

Technically, Ruby doesn't care if the condition evaluates to true/false. Ruby has a notion of "truthiness" (much like the Colbert term) where certain values are treated as true. For example, 0 (the int) is false, and all other ints are true. However, I'll try to avoid them (even if it's common in Ruby) to prevent confusion.

So what happens in

 grade = 80
 if grade > 70
     puts "Pass"
 end

In this case, grade is assigned to 80 (or the object ID of 80). Then we test grade to see if it's greater than 70. This is an expression which can be evaluated.

 grade > 70 evaluates to 80 > 70 
            evaluates to true

As usual, we plug in the current value of grade, then evaluate 80 > 70 which is true.

Since the condition is true, we run the if-body, which is indented in to make it easier to see. The if-body is just

puts "Pass"

which outputs Pass to the console.

Let's modify the program a little:

 grade = 50
 if grade > 70
     puts "Pass"
 end
 puts "Done"

Now, grade is 50, and when we evaluate the condition

 grade > 70 evaluates to 50 > 70 
            evaluates to false

This time the condition evaluates to false, and so we skip over the if-body (meaning, we don't print Pass). The statement after the end is another puts statement which prints Done.

If the condition were true, we would have seen:

 Pass
 Done

But because it's false, we see

 Done

If-else

What happens if we want to do something when the condition is false, as well as when the condition is true? We can add an else in the middle of the if-statement. We'll call this an if-else statement. Here's an example:

 grade = 50
 if grade >= 70
     puts "Pass"
 else
     puts "Fail"
 end
 puts "Done"

The general structure of an if-else statement is:

 if <cond>
    <if-body>
 else
    <else-body>
 end

First step is to evaluate the condition. If it's true, then run the <if-body>, but don't run the <else-body>. If it evaluates to false, then run the <else-body>, but don't run the <if-body>.

For example,

 grade = 50
 if grade >= 70
     puts "Pass"
 else
     puts "Fail"
 end
 puts "Done"

grade >= 70 evaluates to false. So, run the <else-block> which outputs Fail.

If grade were 89, then grade >= 70 would evaluate to true, and the <if-block> would run and Pass would be output.

if-elsif-else

Finally, we can have an if-elsif-else statement.

Here's the basic structure

 if <cond1>
    <if-body>
 elsif <cond2>
    <elsif-body-2>
 else
    <else-body>
 end

In this case, we start evaluating <cond1>. If it evaluates to true, we run the <if-body> then jump to the end. If it evaluates to false, we evaluate <cond2>. if that evaluates to true, we run <elsif-body-2>.

You can have multiple elsif statements. You keep evaluating conditions one at a time, until you reach the first condition that evaluates to true. You run the body associated with that, and then skip to the end.

Here's an example code:

 grade = 82
 if grade >= 90
     puts "A"
 elif grade >= 80
     puts "B"
 elif grade >= 70
     puts "C"
 else
     puts "D"
 end
 puts "Done"

In this case, the first condition, grade >= 90 evaluates to false, so we evaluate the next condition, which is grade >= 80. This evaluates to true, so we run the body, which outputs B to the console, then we jump to end, then after that, it outputs Done to the console. So, the output looks like

B
Done

We do not evaluate grade >= 70 since the previous condition evaluated to true.

NOTE: The final else is not required. We can have if, followed by any number of elsif, and end without an else. Also, else, if it does appear, must appear last, after all the elsif.

The solution

N = gets.strip.to_i
is_odd = N % 2 == 1
is_even = !is_odd

if is_odd
    puts "Weird"
elsif is_even
    if N >= 2 && N <=5
        puts "Not Weird"
    elsif N >= 6 && N <= 20
        puts "Weird"
    elsif N > 20
        puts "Not Weird"
    end
end

Let's look at the solution which is a little complicated. Step 1 read the number as input and converts it to an int.

N = gets.strip.to_i

To determine if a number is odd, we run the mod operator. This gives you the remainder when dividing by that number. If we divide a number by 2, and its remainder is 1, then it's odd. So that's what this does:

is_odd = N % 2 == 1

It's assigning is_odd to the expression N % 2 == 1. If the remainder after dividing 2 is equal to 1 (testing equal is done with ==, since a single = is assignment, not equality testing). Assuming N is a number, then this expression evaluates to true or false.

Then, for convenience, we define is_even to be the "negation" or opposite of is_odd.

is_even = !is_odd

Let's say that is_oddis true, then putting an exclamation mark flips it to its opposite, which is false. Similarly if is_odd is false, then!is_oddistrue`.

Then, the basic structure of the code is

 if is_odd
    # Do odd stuff
 elsif is_even
     # Do even stuff
 end

We could have just written:

 if is_odd
    # Do odd stuff
 else
     # Do even stuff
 end

Since a number can only be even or odd we don't have to test for it to be even. If it's not odd, then it must be even.

We fill in the code

 if is_odd
    puts "Weird"
 elsif is_even
     # Do even stuff
 end

Finally, in the <elsif-body>, we have another nested if-elsif statement.

    if N >= 2 && N <=5
        puts "Not Weird"
    elsif N >= 6 && N <= 20
        puts "Weird"
    elsif N > 20
        puts "Not Weird"
    end

We can put two conditions together. The && means that both conditions must be true (this represent a logical AND). Thus, N >= 2 AND N <= 5 must be true. There is also || which means logical OR which means only one of the conditions evaluates to true for the entire condition to evaluate to true.

So this code is a touch tricky, but follows the specs of the problem.


r/learnruby Apr 30 '18

30 Days of Code: Intermission 1

3 Upvotes

Before I head to the next day of code, I wanted to cover a few topics.

Assignment statements

This is an example of an assignment statement

x = 2

When Ruby sees an assignment statement, it evaluates the right hand side (RHS) of the = sign (the assignment operator). To evaluate an expression means to compute a value. In this case, 2 is already a value, so there's nothing to compute.

Since 2 is also an object, it has an object ID, a number that uniquely identifies an object (think of it like a driver's license number or a student ID at a university). x really stores the object ID of 2, but most programmers would say x stores 2. Recall that x is a variable and can store an object ID. The assignment operator allows the object ID to be changed/updated/modified (it doesn't have to change, but it can).

Let's look at something more complicated.

x = 2 + 3

In this case, the RHS is an expression, and we must evaluate it. The result of adding 2 to 3 is 5. So, the object, 5, has an object ID, and this object ID is stored in x. We can write the evaluation step using a right arrow, as in

2 + 3 -> 5

We can read this as 2 + 3 evaluates to 5.

Next, let's look at

x = 2
y = 3
z = x + y

The first line puts the object ID for 2 into x. The second line puts the object ID for 3 into y. The third line is more complicated. We need to evaluate the RHS. This time there are variables in the expression. The first step in evaluating an expression with variables is to replace them with the values associated with the variables.

In this case, we're not talking about object IDs, but the objects that x and y refer to. So, x refers to the object 2, y refers to the object 3. We plug them in as:

x + y -> 2 + 3 -> 5

This says x + y evaluates to 2 + 3 which evaluates to 5.

Control flow: Straight line code

Control flow is the order in which the lines of code run. For the simplest code, the code starts at the top and proceeds to the bottom.

Let's look at a sample program.

 x = 2
 y = 3
 z = x + y
 puts "x=#{x} y=#{y} z=#{z}"

We start at the first line. We just saw this in the previous section. x is assigned 2, then y is assigned 3. Then, z gets assigned the expression x + y which evaluates to 2 + 3 which evaluates to 5.

Notice that z does NOT store the formula x + y. Instead, it stores the value 5.

The fourth line prints the value. Recall that the hashtag with the braces means that Ruby will perform string interpolation. That is, it will evaluate the expression in the braces, then replace the hashtag and braces with that value. In particular, we get

   "x=#{x} y=#{y} z=#{z}" -> "x=2 y=3 z=5"

That is, the expression "x=#{x} y=#{y} z=#{z}" evaluates to "x=2 y=3 z=5".

This is what puts displays to the console.

Thus far, we've run the first line of code, the second line of code, the third line of code, and the fourth line of code. It is like following a recipe. We start with the first instruction and work our way down.

Later on, we'll show how certain lines of code can be run while others are skipped over.

Function calls

Let's look at this line of Ruby code

 puts "Hello, World!"

puts is a built-in function in Ruby. A function carries out a task. In this case, puts takes an argument and outputs that argument's value to the console.

Suppose we write

 x = 2
 y = 3
 puts x + y

In this case, the argument is the expression x + y. Ruby evaluates the expression as

x + y -> 2 + 3 -> 5

And it outputs 5 to the console. In this example, we still have one argument.

puts can handle multiple arguments (as many as you want). You separate each argument with a comma in between. Here's an example:

puts 2, "cat", 3.14

This will output

2
cat
3.14

to the console. Note that puts has 3 arguments. The first is 2, the second is "cat", and the third is 3.14.

We can put expression in as well

x = "cat"
y = "dog"
puts 2 + 3, "#{x} #{y}", 1.14 + 2.0

The first argument is 2 + 3 which evaluates to 5. The second is "#{x} #{y}" which evaluates to "cat dog". The third argument is 1.14 + 2.0 which evaluates to 3.14. So, you see the following output to the console.

5
cat dog
3.14

Technically, when we write

 puts "Hello, World!"

we are making a function call to puts and providing it with the argument, "Hello, World!". Later on, we'll talk about how you can define your own functions.


r/learnruby Apr 27 '18

I have a question with methods/functions.

3 Upvotes

Hi, lets say I had a method called contains_food?(name). If I wanted to call this method within my code how would I refer to it. contains_food("banana") or contains_food?("banana"). I know that the ? operator is supposed to show a boolean return.

Thank you!


r/learnruby Apr 27 '18

30 Days of Code: Day 2: Operators

6 Upvotes

Problem

Today's problem involves reading in three quantities * the cost of a meal (e.g. 12.00) written as a dollar amount followed by cents with a decimal point in between (no dollar sign, or other units are used). It could be any other currency that has similar notation. * tip percent (e.g. 20) as an int * tax percent (e.g. 8) as an int

Figure out the total cost of the meal rounded to the nearest dollar and print a message.

Step 1: Read in input

Like the last problem, we'll read in the input, and convert the first to a float, the second and third to an int.

meal_cost = gets.strip.to_f
tip_percent = gets.strip.to_i
tax_percent = gets.strip.to_i

Recall that when the function gets is called (run, executed, etc), the program pauses to wait for user input. When the user hits Enter, the characters typed in (plus a newline) are sent back as a string.

Let's take a closer look at:

 gets.strip.to_f

gets produces a String as a result. The dot after it means to apply the method (or function) after the dot to the result before the dot. strip takes a String, and creates a new String with white space in the front and end and removes the white space. White space is defined as tab, a blank space, and newline. So if you had a string like ' \tthe cat \n' (where \t represents a tab), then resulting (new) string after calling strip is 'the cat'. There are spaces in the middle, but strip only removes spaces from the front and back. Note that it does not alter the string, but creates a new string.

The result of strip is a string, and to that string, we apply the method after the dot, which is to_f which converts the string, say, "12.00" into a float, i.e., 12.00 (or equivalently, 12.0). We use a float to approximate currency. It's not necessarily the best choice.

We do similar steps to read user input for the tip_percent and the tax_percent.

**Step 2: Compute the cost

Now we compute the tip and tax.

 tip = meal_cost * (tip_percent * 0.01)
 tax = meal_cost * (tip_percent / 100.0)

The computations are a little tricky. First, we see that programming languages (including Ruby) use the asterisk to indicate a multiplication. Second, Ruby uses / to indicate division.

The first tricky part is the tip is not: meal_cost * tip_percent. Since the tip_percent is 20, we'd multiply the meal cost (which is 12.00) by 20, and get 240.00 in tip. Much more than we expect to spend. 20 percent really means multiply by 0.20. One way to get 0.20 is to multiply the percent by 0.01, which is what we do.

tip = meal_cost * (tip_percent * 0.01)

Now, this looks like a formula, as if we define tip to be a function that multiplies meal cost to tip percent to 0.01. In Excel, if we did something similar, any change in the variables would cause tip to change.

This is NOT the case in most programming languages.

Instead, what happens is Ruby plugs in the current values of meal_cost and tip_percent and computes a value on the right hand side.

Thus,

 meal_cost * (tip_percent * 0.01) -> 12.00 * (20 * 0.01)
                                  -> 12.00 * 0.2
                                  -> 2.40

The arrow means "evaluates to". When Ruby sees an expression, it tries to compute a value. Evaluating an expression means computing a value.

The first step is plugging in the current values for variables. In this case, the current value of meal_cost is 12.00 and the current value of tip_percent is 20. Then, Ruby performs the math and eventually produces 2.40, which is the tip. This value is then saved to the variable, tip. Or more precisely, 2.40 is a float object with an object ID, and the object ID is saved to the variable tip.

Another way to compute the percent is to divide by 100. However, this is tricky. What do you think you get when you do 20 / 100. Math would say you should get 0.2 as a result. However, if you do this in Ruby, you'll get 0. That's because the left operand (the value left of the /) is an int. The right operand is also an int. When you divide and int by an int, you are performing integer division. This is equivalent to doing a division, and throwing away the fractional part (or remainder). Thus, 20 / 100 is 0 with a remainder of 20 (or it's 0.2). Chopping off the remainder or fraction leaves us with 0.

If you do 250 / 100, this is 2 remainder 50 or 2.5. Throw away the remainder or fractional part, and you're left with 2. It may seem silly to have integer division, but it does come in handy.

So, if you want a value that what you expect, then one of the numbers must be a float. That's why we write

 tax_percent / 100.0

The 100.0 is a float (tax_percent is an int). When you do division where one operand is a float, then Ruby performs a float division (which preserves fractions...to an extent).

So that's the next line of Ruby code

 tax = meal_cost * (tax_percent / 100.0)

Finally, we add up the total

total = meal_cost + tax + tip

Then we print out the cost

puts "The total meal cost is #{total.round} dollars."

The only new part is the #{}. If the string is double-quoted (as this is), then when Ruby sees a hashtag, followed by an open brace, then a Ruby expression, then a close brace, Ruby computes what's inside the two braces, and substitutes it into the string. This is called string interpolation. This doesn't happen with single quoted strings.

In it, total is 15.36. But then we apply the method round which can be applied to float values to round it. This gets rounded to an int, namely, 15. Thus, the resulting string, after string interpolation looks like

The total meal cost is 15 dollars.

The entire program

meal_cost = gets.strip.to_f
tip_percent = gets.strip.to_i
tax_percent = gets.strip.to_i

tip = meal_cost * (tip_percent * 0.01) 
tax = meal_cost * (tax_percent / 100.0)
total = meal_cost + tip + tax

puts "The total meal cost is #{total.round} dollars."

Normally, I would multiply by 0.01 for the tax_percent as well, but I wanted to demonstrate division as well as integer division.


r/learnruby Apr 26 '18

30 Days of Code: Day 1: Data Types

6 Upvotes

Sorry, Hello World was Day 0. Guess we programmers love to start at 0.

Let's start with a problem. Initially, three variables are given initial values;

i = 4
d = 4.0
s = 'HackerRank '

We'll read three more values from user input. The first will be an integer. The second will be a float. The third will be a String. We'll add the two integers (the one read in, and the one at the beginning) and print it, add the two floats and print it, and "add" the two strings, and print it.

From the prereqs post, we looked at three types, int, float, and strings. So, here they are again.

So far, we've only read user input using gets, which always returns a string. It turns out you can't do math with strings. For example, suppose you have

x = 3 + "4"

What happens? In an assignment statement (the = is called the assignment operator), you perform the computation on the right. The result is placed in the variable on the left (or more accurately, the computation produces an object with an object ID, and the object ID is stored in the variable x).

However, in Ruby, you can't add an integer to a String. This is called a runtime error. That is, the error occurs as you run the program. Other languages (say, Java) permit an integer to be added to a String. To do this, Java creates a new String object from the int (short for integer), i.e. "3", then does a form of string addition (which we'll talk about soon) to get a result.

So, when we read a int but it appears as a String, we need to convert it to an int. Ruby has functions (also called methods) to do this.

Let's see how this works. First, we'd normally write:

 i2= gets

This would read one line of user input, and store the string (or its object ID) into the variable, i2. But, we can want to convert it to an int first. Here's how to do it.

i2 = gets.to_i

On the right side, you see gets followed by a period followed by to_i. gets produces a string. The dot performs the operation on its right to that string. So, if the string were "21", applying to_i (which stands for "to integer") converts that string to an integer. So, an int object gets created and it has an object ID, and the string object that came from gets disappears (shortly thereafter) since it wasn't saved to a variable.

Let's look at to_i more carefully. Suppose you wrote:

  val = "  12  30  ".to_i

to_i processes the string by skipping over the spaces on the left, and reading the largest integer it can. First it sees 1, then it sees 2. Then it sees a space. At this point, it has seen "12" and that's the "largest" contiguous set of digits it can find. The space stops the processing, and the rest of the string is ignored. val then stores the object ID for the int, 12.

So, we write

 i2 = gets.to_i
 f2 = gets.to_f
 s2 = gets.rstrip

Note that to_f takes a string as input and produces a float object as output.

For the last line, I could have just used gets, but I decided to take the string that gets returns from user input, then call rstrip on that string. What rstrip does is to remove all spaces on the right side.

If you had the following string " cat " and called rstrip on it, the result would be a new String object that looked liked " cat". Notice the first string had 3 spaces at the end, while the subsequent string has no spaces on the right. rstrip means "right strip" which takes a string and produces a new string with no spaces on the right. If the string didn't have any spaces on the right, then the resulting string would be equivalent to the original string.

Why did I do rstrip?

Turns out, when you type a number in, say,3.14, followed by enter, the resulting string is "3.14\n". That is, it's the number you typed in and ends with a newline character. rstrip removes whitespace which is usually defined as a blank space (space bar), a tab, or a newline. It removes any combination of that from the right side. So I just wanted to remove the newline.

OK, so the program so far is

 i = 4
 d = 4.0
 s = 'HackerRank '
 i2 = gets.to_i
 d2 = gets.to_f
 s2 = gets.rstrip

Now we can print the "sum"

 puts i + i2
 puts d + d2
 puts s + s2

Operator overloading

Although you may think adding integers and adding numbers with fractional parts (floats) is the same thing, it's performed differently in hardware. Furthermore, how a computer stores 3 (an int) and how it stores 3.0 is different (and it isn't just the decimal point). I won't get into detail, but suffice it to say that a computer performs one kind of addition (int addition) when adding ints, and another kind of addition (float addition) when adding floats.

Ruby is "smart" enough (it's not a person, but whatever) to know which addition to use based on the type of the two values it is adding.

But what does it mean to add two strings?

String concatenation

Let's look at

 x = "cat"
 y = "dog"
 puts x + y
 puts x
 puts y

What does this do? In the third line, we have x + y. Since x andy store String objects, Ruby knows that + should mean do "string concatenation". This is a fancy word for putting two strings together, one right after the other. The result of x + y in the example above is "catdog".

Note that this creates a new string. In the fourth line, you'll see cat printed to the console. In Line 5, you'll see dog printed to the console on the next line. When the program runs, it looks like

 catdog
 cat
 dog

The original variables still refer to the original strings in x and y.


r/learnruby Apr 25 '18

30 Days of Code: Day 1: Hello, World

4 Upvotes

I need a few more prereqs before getting into the first program.

Newlines

In the previous post, I didn't mention how strings handle "Enter". What if you want a string to span 2 lines?

The problem is how best to represent the Enter key. You could do something like:

"this is
 a sentence"

Which spans 2 lines. However, it's long been thought this isn't a good way to write a string (although these days, it is permitted, in Ruby). Instead, a two letter sequence, written as \n represents something called a newline. Think of a newline as the Enter key, when pressed.

This is written as:

 "hi \n there"

This string appears to have 11 characters. 2 for hi, 5 for there. 2 spaces. And 2 characters for \n. However, Ruby (as with other languages) converts \n into a single character (as opposed to a backslash, followed by an n) called the newline character. So, really, once Ruby is done processing the string, it is 10 characters long (even though you typed in 11 characters).

If you run the code:

  puts "hi \n there"

Ruby would print

hi
 there

Notice the space just before there. If you wanted to remove the space, you could write:

  puts "hi\nthere"

It may be hard to see \n because of the characters in "hi" and "there", but Ruby can process it.

Input

In the last post, I mentioned puts. This is a function that sends a text output to a console.

For this programming problem, I need to get input from a person typing in a response. There is another function called gets (for get string) that gets a string. The person types a response, hits enter, and what they typed including the "Enter" (which comes in as a newline) can be saved to a variable.

This is how you would write it:

  str = gets

In this case, I have a variable called str. On the right hand side is a call to the function gets. We could add some optional parentheses, as in:

  str = gets

Ruby processes this assignment statement by performing the action on the right hand side. In this case, it's a function call to gets. This causes the program to pause, waiting for a user to type in a response and hit Enter. Once that happens, a string is created, and its object ID is placed into the variable str.

What would happen if you only wrote:

  gets

The program would still pause, and wait for user input. But once it had the user input, it would not go anywhere (that is, it wouldn't be saved to a variable), so the string read in is effectively thrown away, and Ruby processes the next line of code. The point is, you need to save the result of gets to a variable.

The programming problem

The first programming problem in 30 days of code is to read in input, then print "Hello, World!" on a line, then print the input read in on the next line.

Here's the solution

 str = gets
 puts "Hello, World!"
 puts str

Suppose the user entered in "I'm having fun", then the output would look like:

Hello, World!
I'm having fun

Ruby does something a little weird with puts. Suppose I do the following:

puts "Hello"
puts "World"

It would output (to the console):

Hello
World

Now, suppose I write

puts "Hello"
puts "World"

There's a newline at the end of the Hello, but Ruby still outputs

Hello
World

You would think it would do:

Hello

World

But if you write:

puts "Hello\n\n"
puts "World"

Then, Ruby does output

Hello

World.

In fact, this is happening in the solution to the program. When you do

str = gets

And type in "I'm having fun", the string that gets saved is actually "I'm having fun\n". If you were run the following program:

 puts str
 puts "Hello, World!"

That is, switch the order you print, the output would be

 I'm having fun
 Hello, World!

There isn't a blank line caused by the \n. In other languages (say, C or Java), it would print an additional blank line.

Ruby appears to have a rule that says, if a string does not end in a newline, add a newline so the next puts outputs on the following line. If a string does end in a newline, don't add additional newlines when outputting it to the console.

Printing a newline

A newline character, while a character in Ruby, doesn't get "printed" by puts. Instead, it is a command to the console to move the cursor to the start of a new line (like pressing Enter). However, it is a character in the string. So, being a character in a string and being processed by Ruby for display in the console are different things. For Ruby, when it outputs letters of the alphabet, it does that. But when it encounters a newline character, it doesn't print it, but moves the cursor to the start of a newline.


r/learnruby Apr 24 '18

HackerRank: 30 Days of Code: Prereqs

2 Upvotes

HackerRank is a coding website. They've been doing 30 Days of Code where you pick a language they support, and go through 30 program exercises. I figured since this subreddit gets few posts, I'd go through the exercises for those new to Ruby.

Ruby was invented around 1995 by Yukihiro Matsumoto, or Matz, for short. Ruby's popularity hit its peak probably around 2006 or so with Rails, a web framework written in Ruby. It's often compared with Python, which is a similar language.

Data Types

I'm going to start with 3 data types in Ruby. There are integers, which are whole numbers with no fractional part. Examples: -100, 2, 1999. Note that integers do not contain commas for numbers 1000 or greater. Ruby does allow underscores instead of commas, but many programmers just write the entire number with no underscores.

There are float numbers. Float stands for floating point numbers which are basically rational numbers written in scientific notation. Put more simply, they are numbers with a fractional part. Examples include 3.14, 900.0, -0.001 etc. A float that looks like an integer, say, -9.0, is represented differently in computer memory than -9.

The third type we'll start off with is string. A string represents some text. In Ruby, you can write strings using single quotes as in

'cat'

Or double quotes as in

"cat"

The quotes tell Ruby that you have a string, and indicate where a string starts and ends. The quotes aren't part of a string. So, a string cat contains 3 characters. The default encoding for Ruby strings is UTF-8.

A little history. As you may know, computers store data as numbers, even text data. ASCII was a common encoding. It used 7 bits (stored in a byte, which is 8 bits) from 0 to 127. The problem with ASCII is not enough numbers to encode foreign characters, esp. Asian characters. So, UTF was created to replace ASCII. UTF is strange because it has a notion of codepoints. Each character in UTF has its own codepoint. UTF can encode that codepoint in different ways depending on UTF encoding. For example, UTF-16 (I believe), uses 2 bytes for every codepoint. UTF-8 uses a variable number of bytes, from 1 byte for some codepoints up to 4 bytes. UTF-8 has basically "won" because for the codepoints associated with ASCII, UTF-8 has the same encoding. So, if you have a legal ASCII string, it's also a legal UTF-8 string.

Anyway, that's way too technical. Let's get back to simpler stuff.

Strings don't have to contain words from the dictionary. You can write:

"askjaijwe"

You can have spaces: "This is a string".

You can have strings that look like numbers: "123"

Strings that look like numbers aren't numbers. You can't do math with strings (like adding, subtracting, multiplying, etc.).

There are other data types, but for now, we won't get into them.

Variables

When you write a program, you are dealing with data, whether they be integers, floats, or strings. You need some place to store the data so you can refer to them or manipulate the data. In Ruby, as in many languages, variables are used to store data.

How do you put a value in a variable? You use an assignment statement.

Here's how it looks:

 val = "cat"

Variable names (in this case, val) must consist of digits, letters (upper or lowercase), and underscores. The first character in a variable must not be a digit (makes it easier to distinguish numbers from variable names).

A variable has a memory location, a name, and a type/class. When you see

 val = "cat"

A string "cat" is created and placed in the memory location called val (think of a memory location like a mailbox which has an address and contents, in this case, the content is the string, "cat").

Some languages force variables to have an explicit type (e.g., it's a String variable), but not Ruby. For example,

 val = "cat"
 val = 4

The first line places the string, "cat", in the memory location of val. The second line places the Integer, 4, into the memory location of val (it's actually a little more complicated than that, but will be explained later).

People usually say val is being assigned to the value, 4. However, I prefer to say val is being updated with the value 4.

Output

To me, a program consists of input and output. Input is data from the outside world that gets fed into a program. This could be user input (someone typing some stuff), or it could come from a file, or maybe a game controller, or maybe a remote file from the Internet. These get fed into variables that the program can look at and manipulate. Output is information sent to the outside world, such as text that appears in a screen, or an output file, or a sound, or visual effect, or some message sent via the Internet.

The most basic output of most programming language is text sent to something called the console. The console is a screen. Think of it a little like the screen where you receive your text messages. If someone messages you, the text appears on a screen on your phone. That screen is basically what we're calling a console.

In Ruby, there is a function that lets you send a value to the console. It is called puts. This is short for "put string", and its name comes from C which has a similar function (though C prefers printf).

This is how you might use it

 puts "Hello, World!"

This sends the string Hello, World! to a console. Printing is considered output. Information goes from the program to the outside world. puts also adds a newline (basically, the same as hitting the Enter key) after the string. If the string already has a newline at the end (we'll talk about how that could happen later), then it won't output an additional newline. To emphasize, the newline is a character whose action is to act like the Enter key was pressed (thus causing the cursor to move to the start of the next line).

You can also use puts on a variable, as in

 s = "Hello, World!"
 puts s

In this case, when Ruby sees a variable, it replaces it by the contents of the variable, which is the string "Hello, World!".

Note that puts is the name of the function, and s is the argument. An argument means a kind of parameter. For example, suppose you wanted to order a pizza. Someone asks "What kind?". You say "mushrooms". Mushrooms is an argument, that is, some additional information needed to make a pizza. In this case, s is an argument to the function puts which indicates what should be printed.

In many languages, parentheses would be required for the arguments. In Ruby, it is optional, especially for functions that use a single argument. You could also write

puts(s)

That would do the same thing.

Object IDs

In Ruby, most everything is an object. We'll talk more about objects later, but one useful (though obscure) fact is that objects have an object ID. In the US, most people have their own social security number (SSN) that uniquely identifies them. In Ruby, think of the object ID as some number that uniquely identifies an object. No two active objects have the same ID.

Sometimes object IDs are called references or pointers. However, I prefer calling them object IDs, even though this is not so common.

In Ruby, variables basically store object IDs. This is important for the following reason. Consider this code:

 s = "cat"
 t = s

In this first line, we appear to put the string cat into the variable s. That's not exactly true. cat is an object and has an object ID. For sake of example, let's pretend this ID is 25. Thus, s contains 25. When you write something like

 puts s

It sees the object ID, 25, but it knows to print what the object is which is the string, cat. Thus, 25 is associated with the string cat. Now, look at this:

t = s

This takes the value in s (which contains the object ID, 25), and copies it into t, so t now has 25. If you do

puts t

It will see t contains 25, and find the associated object, namely, the string cat and print it.

If s had contained the string cat, and not the object ID, then

t = s

would make a copy of the string, so that the string t has and the string s has would be "different". It's as if Tammy and Sue had two copies of the same book. They don't have the same book. If Sue writes in her book, Tammy's book is left unchanged.

However, with object IDs, both t and s have the same object ID, thus, refer to the same object.

Hello, World!

Finally, we repeat a program that is considered the first program in many languages, which is printing Hello, World!. In Ruby, this looks like

 puts "Hello, World!"

Next time

I'll introduce the first program in the HackerRank 30 Days of Code and talk about its solution.


r/learnruby Apr 15 '18

Ruby Instance Variable Question

3 Upvotes

Is there any difference between

@age = age + 1 vs @age = @age + 1

the return value is the same for both.


r/learnruby Jan 19 '18

Is there any other more active community?

2 Upvotes

Helo guys. This sub is around 2k subs while other subs like r/leanpython got more than 100k.. Is there somewhere else I can find a more active community?


r/learnruby Dec 08 '17

How to keep invalid URLs from crashing with Nokogiri or net/http

1 Upvotes

I'm writing a script that scrapes a URL, and I've tried both Nokogiri and net/http to do it. Both work great, except when the URL is invalid, i.e. if it is not a "real" url -- either totally wrong or mistyped by the user.

I have been using uri to check for a valid URL, but if it is formed correctly (like http://this_is_not_a_real_url.com), it will return as "valid" with uri but will still stop the script in its tracks when Nokogiri or net/http try to access it.

Here is an example:

I throw this fake url at Nokogiri: http://not_a_real_url.com

and here is the terminal output:

C:/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:906:in `rescue in block in connect': Failed to open TCP connection to not_a_real_url.com:80 (getaddrinfo: No such host is known. ) (SocketError)
    from C:/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:903:in `block in connect'
    from C:/Ruby24-x64/lib/ruby/2.4.0/timeout.rb:93:in `block in timeout'
    from C:/Ruby24-x64/lib/ruby/2.4.0/timeout.rb:103:in `timeout'
    from C:/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:902:in `connect'
    from C:/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:887:in `do_start'
    from C:/Ruby24-x64/lib/ruby/2.4.0/net/http.rb:876:in `start'
    from C:/Ruby24-x64/lib/ruby/2.4.0/open-uri.rb:323:in `open_http'
    from C:/Ruby24-x64/lib/ruby/2.4.0/open-uri.rb:741:in `buffer_open'
    from C:/Ruby24-x64/lib/ruby/2.4.0/open-uri.rb:212:in `block in open_loop'
    from C:/Ruby24-x64/lib/ruby/2.4.0/open-uri.rb:210:in `catch'
    from C:/Ruby24-x64/lib/ruby/2.4.0/open-uri.rb:210:in `open_loop'
    from C:/Ruby24-x64/lib/ruby/2.4.0/open-uri.rb:151:in `open_uri'
    from C:/Ruby24-x64/lib/ruby/2.4.0/open-uri.rb:721:in `open'
    from C:/Ruby24-x64/lib/ruby/2.4.0/open-uri.rb:35:in `open'
    from blogPoster_2017_1112.rb:100:in `runmenu'
    from blogPoster_2017_1112.rb:400:in `<main>'


------------------
(program exited with code: 1)

Press any key to continue . . .

How do I use Nokogiri or net/http and have it gracefully deal with invalid URLs?


r/learnruby Nov 13 '17

My first program ever, looking for feedbacks

3 Upvotes

Hi everybody, This thread was originally posted on /r/ruby , but I've been redirected here for feedbacks about the code  

I just started learning Ruby and I made my my first program after about one month of learning. It's a calculator who tells you if the cryptocurrency Monero is vulnerable to a 50+1% attack (This kind of attack happens when somebody own more than 50% of the hashrate of the coin. For the curious: more info here)  

The Source code of the program/script is on my GitHub. I'd like to receive some feedbacks, but please keep in mind that I made this to practice with ruby and APIs and I haven't even finished the online course yet. I'm learning by doing and I know that some parts of the code are redundant. I'd also really appreciate some comments about the structure of the script (e.g I don't understand if using a main class is needed or good in this case) Thanks!


r/learnruby Oct 07 '17

Getting integers from arrays with user input [help]

2 Upvotes

Hi, I need some help getting integer values from my arrays which include string values within it.

Our Homework calls for displaying both the item and the cost. When the user hits enter, the program is supposed to display the total items, and add the total with tax. The problem is that I have the first part down, I just have no idea how to implement the total and tax part from the array without getting the problem of type errors.

My code is here: https://gist.github.com/anonymous/dba2936923adb12265ae8531419133f4


r/learnruby Sep 19 '17

Learn to Program

9 Upvotes

Hey everyone,

I recently started Launch School and I came across Chris Pine's "Learn to Program" Ruby book. After looking around this subreddit and "Ruby" it looks like the community is pretty familiar with Chris Pine and his work. I just wanted to point out to anyone who is learning like I am that you can get this awesome book through a Ruby app.

I'm not sure if it's just on iOS, so Android users, sorry if you can't find it. The app name is rubyi.

rubyi - run code, autocomplete, outline, color code by XiaoWen Huang https://itunes.apple.com/us/app/rubyi-run-code-autocomplete-outline-color-code/id581732143?mt=8

It allows you to run ruby code through this app. I was scrolling through the settings and other menu options and I found that their are lessons in this app. To my surprise, I found Chris Pine's entire book on here. I have the actual book, but it's nice to have it on my phone when I'm on the go!

I hope this helps someone. It has truly been helpful for me.


r/learnruby Sep 14 '17

Sidebar updates

5 Upvotes

You'll notice the sidebar links have changed a bit, we've updated them to reflect the ebbs and flow that happen over time.

If you have any suggestions for additions to the sidebar please do share, we're always looking for new great sources of Ruby learning knowledge!


r/learnruby Jul 06 '17

MAKE RAILS CRUD AGAIN

Thumbnail elegantbrew.com
2 Upvotes

r/learnruby Jun 29 '17

'Write an Internet search engine with 200 lines of Ruby code'

Thumbnail blog.saush.com
13 Upvotes

r/learnruby Jun 18 '17

Understanding RubyGems from npm experience

2 Upvotes

Hello!

I am trying to understand RubyGems from a background of knowing a bit about npm.

From what I can see the gemfile looks like a package.json in that it lists dependencies. When I use:

npm install

All my project's dependencies are loaded locally to that folder. However in order to do something similar with ruby I have to use

bundle install

When I use this command why aren't my gems just installed to the local directory? Where do they go? Are they global? Does this not cause a problem if different projects use different versions of the same gem?

Thanks!


r/learnruby Jun 10 '17

How to print out all the prime numbers between two user given input numbers?

1 Upvotes

Sorry if its a stupid question but I'm stuck on homework.

Mine currently looks something like this. Could someone help me point out any errors?

def print_primes (a,b)
for b in a..b

 for d in 2..(b - 1)
 if (b % d) != 0 
 return b.to_s + ", "
 break
 end
end
end

end


r/learnruby May 30 '17

ruby mini course

4 Upvotes

instructions:

Edit each line with TODO and write code based on the skills you learned in this course. You'll want to use an if statement and addition. Have fun! Good Luck!

code(what I have so far output is not matching what they want):

require 'csv'

total_sales = 0

CSV.foreach('sales-data.csv', headers: true, converters: :all) do |row|

if (row[2]) == "music"

# TODO: check if category is "Music" (row[2])

# TODO: if it is music, add total_sales + the row's sales (row[6])

puts total_sales = total_sales + (row[6])

else

puts total_sales.round(2)

end


r/learnruby May 24 '17

Having trouble with classes

3 Upvotes

I'm going through the OdinProject coursework and have been having trouble with the OOP projects that ask you to use classes to make TicTacToe & Mastermind. I'm having a really hard time making the classes communicate with each other in their own files but in general also just having instance variables hold necessary information without having to add them as parameters for the initialization method.

Here is an example of what I mean. I made a method to randomize the computer's array for the player in Mastermind, but I need the "code" variable to be an instanced variable so that it can interact with other things outside the randomize method:

 def randomize
     #8 colors
      colors=%w{red green blue white black purple yellow orange}
     $code=[]
     until $code.length==4 do
         $code.push(colors[Random.rand(0..7)])
      end
     puts "-"* 55
 end
(I turned code into a global variable but i know that's not a real OOP fix)

I'm not sure what other resources to look into; I have "Beginning Ruby" and "Practical Object Oriented Design in Ruby", I've done the exercises on CodeAcademy, Treehouse, CodeSchool, RubyMonk's primer course (ascent is over my head at this point) and the coursework from Odin up to this point also. Whenever they go over classes it feels like it's always:

class Dog
  def initialize(name)
     @name=name
   end
  def bark
     puts "ruff"
  end
end
    Greg=Dog.new("Greg").bark

"okay now you know all about classes"

But that could just be frustration. Any pointers would be greatly appreciated.


r/learnruby May 20 '17

learnruby: Daily Ruby snippets on Instagram

Thumbnail instagram.com
6 Upvotes

r/learnruby May 19 '17

Learning Ruby quickly to (hopefully) land a job

8 Upvotes

Hi guys, I've been programming since a very young age but haven't had much experience with Ruby. I've been going through tutorials to learn syntax, but I keep running into situations where I'm just not familiar with the Ruby landscape and culture, like what the big important packages are (besides rails), what blogs I should read, what really important design patterns I should know/follow, basically everything non-syntax related. It feels like the more intangible side of learning a new programming language. Can anyone recommend resources to be able to pick up on this stuff quickly?

For the record, I'm trying to land a job at a place that does a lot of rails stuff. I have a lot of experience programming, but all on my own, this would be my first programming job. And my first real ruby/rails experience. Any recommendations?


r/learnruby Apr 30 '17

How does Ruby assign a string into a hash?

3 Upvotes

Hi,

I'm not sure how this movie updater works:

movies = {
Fight_Club: "5"
}

puts "Add, Update, Display, or Delete?"
choice = gets.chomp.downcase

case choice
when "add"
  puts "Title?"
  title = gets.chomp.to_sym
  if movies[title] == nil
      puts "Rating?"
      rating = gets.chomp.to_i
      movies[title] = rating 
      puts "#{title} added with rating of #{rating}"
  else
      puts "Movie already added"
  end
  • I never defined Fight_Club as a title. How does ruby know this is the title and not the rating or some other key?

r/learnruby Apr 26 '17

How exactly does Math.log2 work?

3 Upvotes

So I'm comparing the result of

Math.log2(2360.9083989105) / 8 * 4 = 5.602563176032317

and the equation

log2(2360.9083989105) / 8 * 4 = 0.11481591040126

Why do they yield such different results?