Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Some simple Ruby questions - iterators, blocks, and symbols

Tags:

ruby

My background is in PHP and C#, but I'd really like to learn RoR. To that end, I've started reading the official documentation. I have some questions about some code examples.

The first is with iterators:

class Array
  def inject(n)
    each { |value| n = yield(n, value) }
    n
  end

  def sum
    inject(0) { |n, value| n + value }
  end

  def product
    inject(1) { |n, value| n * value }
  end
end

I understand that yield means "execute the associated block here." What's throwing me is the |value| n = part of the each. The other blocks make more sense to me as they seem to mimic C# style lambdas:

public int sum(int n, int value)
{
    return Inject((n, value) => n + value);
}

But the first example is confusing to me.

The other is with symbols. When would I want to use them? And why can't I do something like:

class Example
  attr_reader @member

  # more code
end
like image 835
Major Productions Avatar asked Jan 06 '12 19:01

Major Productions


People also ask

How are symbols used in Ruby?

Symbols are objects that can be passed around like any other Ruby object. They can also be used to pass values to methods, such as in getter and setter methods in class definitions: These are just a few examples of when to use symbols in Ruby.

What are Ruby iterators?

“Iterators” is the object-oriented concept in Ruby. In more simple words, iterators are the methods which are supported by collections(Arrays, Hashes etc.). Collections are the objects which store a group of data members. Ruby iterators return all the elements of a collection one after another.

What are Ruby blocks?

Ruby blocks are little anonymous functions that can be passed into methods. Blocks are enclosed in a do / end statement or between brackets {} , and they can have multiple arguments.

What does .each mean in Ruby?

The each() is an inbuilt method in Ruby iterates over every element in the range. Syntax: range1.each(|el| block) Parameters: The function accepts a block which specifies the way in which the elements are iterated. Return Value: It returns every elements in the range.


1 Answers

In the inject or reduce method, n represents an accumulated value; this means the result of every iteration is accumulated in the n variable. This could be, as is in your example, the sum or product of the elements in the array.

yield returns the result of the block, which is stored in n and used in the next iterations. This is what makes the result "cumulative."

a = [ 1, 2, 3 ]
a.sum  # inject(0) { |n, v| n + v }
# n == 0; n = 0 + 1
# n == 1; n = 1 + 2
# n == 3; n = 3 + 3
 => 6

Also, to compute the sum you could also have written a.reduce :+. This works for any binary operation. If your method is named symbol, writing a.reduce :symbol is the same as writing a.reduce { |n, v| n.symbol v }.

attr and company are actually methods. Under the hood, they dynamically define the methods for you. It uses the symbol you passed to work out the names of the instance variable and the methods. :member results in the @member instance variable and the member and member = methods.

The reason you can't write attr_reader @member is because @member isn't an object in itself, nor can it be converted to a symbol; it actually tells ruby to fetch the value of the instance variable @member of the self object, which, at class scope, is the class itself.

To illustrate:

class Example
  @member = :member
  attr_accessor @member
end

e = Example.new
e.member = :value
e.member
 => :value

Remember that accessing unset instance variables yields nil, and since the attr method family accepts only symbols, you get: TypeError: nil is not a symbol.

Regarding Symbol usage, you can sort of use them like strings. They make excellent hash keys because equal symbols always refer to the same object, unlike strings.

:a.object_id == :a.object_id
 => true
'a'.object_id == 'a'.object_id
 => false

They're also commonly used to refer to method names, and can actually be converted to Procs, which can be passed to methods. This is what allows us to write things like array.map &:to_s.

Check out this article for more interpretations of the symbol.

like image 70
Matheus Moreira Avatar answered Oct 01 '22 21:10

Matheus Moreira