Some simple Ruby questions - iterators, blocks, and symbols - 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

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.

For the definition of inject, you're basically setting up chained blocks. Specifically, the variable n in {|value| n = yield(n, value)} is essentially an accumulator for the block passed to inject. So, for example, for the definition of product, inject(1) {|value| n * value}, let's assume you have an array my_array = [1, 2, 3, 4]. When you call my_array.product, you start by calling inject with n = 1. each yields to the block defined in inject, which in turns yields to the block passed to inject itself with n (1) and the first value in the array (1 as well, in this case). This block, {|n, value| n * value} returns 1 == 1 * 1, which is set it inject's n variable. Next, 2 is yielded from each, and the block defined in inject block yields as yield(1, 2), which returns 2 and assigns it to n. Next 3 is yielded from each, the block yields the values (2, 3) and returns 6, which is stored in n for the next value, and so forth. Essentially, tracking the overall value agnostic of the calculation being performed in the specialised routines (sum and product) allows for generalization. Without that, you'd have to declare e.g.
def sum
n = 0
each {|val| n += val}
end
def product
n = 1
each {|val| n *= val}
end
which is annoyingly repetitive.
For your second question, attr_reader and its family are themselves methods that are defining the appropriate accessor routines using define_method internally, in a process called metaprogramming; they are not language statements, but just plain old methods. These functions expect to passed a symbol (or, perhaps, a string) that gives the name of the accessors you're creating. You could, in theory, use instance variables such as #member here, though it would be the value to which #member points that would be passed in and used in define_method. For an example of how these are implemented, this page shows some examples of attr_* methods.

def inject(accumulator)
each { |value| accumulator = yield(accumulator, value) }
accumulator
end
This is just yielding the current value of accumulator and the array item to inject's block and then storing the result back into accumulator again.
class Example
attr_reader #member
end
attr_reader is just a method whose argument is the name of the accessor you want to setup. So, in a contrived way you could do
class Example
#ivar_name = 'foo'
attr_reader #ivar_name
end
to create an getter method called foo

Your confusion with the first example may be due to your reading |value| n as a single expression, but it isn't.
This reformatted version might be clearer to you:
def inject(n)
each do |value|
n = yield(n, value)
end
return n
end
value is an element in the array, and it is yielded with n to whatever block is passed to inject, the result of which is set to n. If that's not clear, read up on the each method, which takes a block and yields each item in the array to it. Then it should be clearer how the accumulation works.
attr_reader is less weird when you consider that it is a method for generating accessor methods. It's not an accessor in itself. It doesn't need to deal with the #member variable's value, just its name. :member is just the interned version of the string 'member', which is the name of the variable.
You can think of symbols as lighter weight strings, with the additional bonus that every equal label is the same object - :foo.object_id == :foo.object_id, whereas 'foo'.object_id != 'foo'.object_id, because each 'foo' is a new object. You can try that for yourself in irb. Think of them as labels, or primitive strings. They're surprisingly useful and come up a lot, e.g. for metaprogramming or as keys in hashes. As pointed out elsewhere, calling object.send :foo is the same as calling object.foo
It's probably worth reading some early chapters from the 'pickaxe' book to learn some more ruby, it will help you understand and appreciate the extra stuff rails adds.

First you need to understand where to use symbols and where its not..
Symbol is especially used to represent something. Ex: :name, :age like that. Here we are not going to perform any operations using this.
String are used only for data processing. Ex: 'a = name'. Here I gonna use this variable 'a' further for other string operations in ruby.
Moreover, symbol is more memory efficient than strings and it is immutable. That's why ruby developer's prefers symbols than string.
You can even use inject method to calculate sum as (1..5).to_a.inject(:+)

Related

How does a code block in Ruby know what variable belongs to an aspect of an object?

Consider the following:
(1..10).inject{|memo, n| memo + n}
Question:
How does n know that it is supposed to store all the values from 1..10? I'm confused how Ruby is able to understand that n can automatically be associated with (1..10) right away, and memo is just memo.
I know Ruby code blocks aren't the same as the C or Java code blocks--Ruby code blocks work a bit differently. I'm confused as to how variables that are in between the upright pipes '|' will automatically be assigned to parts of an object. For example:
hash1 = {"a" => 111, "b" => 222}
hash2 = {"b" => 333, "c" => 444}
hash1.merge(hash2) {|key, old, new| old}
How do '|key, old, new|' automatically assign themselves in such a way such that when I type 'old' in the code block, it is automatically aware that 'old' refers to the older hash value? I never assigned 'old' to anything, just declared it. Can someone explain how this works?
The parameters for the block are determined by the method definition. The definition for reduce/inject is overloaded (docs) and defined in C, but if you wanted to define it, you could do it like so (note, this doesn't cover all the overloaded cases for the actual reduce definition):
module Enumerable
def my_reduce(memo=nil, &blk)
# if a starting memo is not given, it defaults to the first element
# in the list and that element is skipped for iteration
elements = memo ? self : self[1..-1]
memo ||= self[0]
elements.each { |element| memo = blk.call(memo, element) }
memo
end
end
This method definition determines what values to use for memo and element and calls the blk variable (a block passed to the method) with them in a specific order.
Note, however, that blocks are not like regular methods, because they don't check the number of arguments. For example: (note, this example shows the usage of yield which is another way to pass a block parameter)
def foo
yield 1
end
# The b and c variables here will be nil
foo { |a, b, c| [a,b,c].compact.sum } # => 1
You can also use deconstruction to define variables at the time you run the block, for example if you wanted to reduce over a hash you could do something like this:
# this just copies the hash
{a: 1}.reduce({}) { |memo, (key, val)| memo[key] = val; memo }
How this works is, calling reduce on a hash implicitly calls to_a, which converts it to a list of tuples (e.g. {a: 1}.to_a = [[:a, 1]]). reduce passes each tuple as the second argument to the block. In the place where the block is called, the tuple is deconstructed into separate key and value variables.
A code block is just a function with no name. Like any other function, it can be called multiple times with different arguments. If you have a method
def add(a, b)
a + b
end
How does add know that sometimes a is 5 and sometimes a is 7?
Enumerable#inject simply calls the function once for each element, passing the element as an argument.
It looks a bit like this:
module Enumerable
def inject(memo)
each do |el|
memo = yield memo, el
end
memo
end
end
And memo is just memo
what do you mean, "just memo"? memo and n take whatever values inject passes. And it is implemented to pass accumulator/memo as first argument and current collection element as second argument.
How do '|key, old, new|' automatically assign themselves
They don't "assign themselves". merge assigns them. Or rather, passes those values (key, old value, new value) in that order as block parameters.
If you instead write
hash1.merge(hash2) {|foo, bar, baz| bar}
It'll still work exactly as before. Parameter names mean nothing [here]. It's actual values that matter.
Just to simplify some of the other good answers here:
If you are struggling understanding blocks, an easy way to think of them is as a primitive and temporary method that you are creating and executing in place, and the values between the pipe characters |memo| is simply the argument signature.
There is no special special concept behind the arguments, they are simply there for the method you are invoking to pass a variable to, like calling any other method with an argument. Similar to a method, the arguments are "local" variables within the scope of the block (there are some nuances to this depending on the syntax you use to call the block, but I digress, that is another matter).
The method you pass the block to simply invokes this "temporary method" and passes the arguments to it that it is designed to do. Just like calling a method normally, with some slight differences, such as there are no "required" arguments. If you do not define any arguments to receive, it will happily just not pass them instead of raising an ArgumentError. Likewise, if you define too many arguments for the block to receive, they will simply be nil within the block, no errors for not being defined.

Calculating totals from two different hashes

I have two hashes:
For example, one contains a list of dishes and their prices
dishes = {"Chicken"=>12.5, "Pizza"=>10, "Pasta"=>8.99}
The other is a basket hash i.e. I've selected one pasta and two pizzas:
basket = {"Pasta"=>1, "Pizza"=>2}
Now I am trying to calculate the total cost of the basket but can't seem to get my references right.
Have tried
basket.inject { |item, q| dishes[item] * q }
But keep getting the following error
NoMethodError: undefined method `*' for nil:NilClass
basket.inject { |item, q| dishes[item] * q }
Let's look at the documentation for Enumerable#inject to see what is going on. inject "folds" the collection into a single object, by taking a "starting object" and then repeatedly applying the binary operation to the starting object and the first element, then to the result of that and the second element, then to the result of that and the third element, and so forth.
So, the block receives two arguments: the current value of the accumulator and the current element, and the block returns the new value of the accumulator for the next invocation of the block. If you don't supply a starting value for the accumulator, then the first element of the collection is used.
So, during the first iteration here, since you didn't supply a starting value for the accumulator, the value is going to be the first element; and iteration is going to start from the second element. This means that during the first iteration, item is going to be ['Pasta', 1] and q is going to be ['Pizza', 2]. Let's just run through the example in our heads:
dishes[item] * q # item is ['Pasta', 1]
dishes[['Pasta', 1]] * q # q is ['Pizza', 2]
dishes[['Pasta', 1]] * ['Pizza', 2] # there is no key ['Pasta', 1] in dishes
nil * ['Pizza', 2] # nil doesn't have * method
Ergo, you get a NoMethodError.
Now, I believe, what you actually wanted to do was something like this:
basket.inject(0.0) {|sum, (item, q)| sum + dishes[item] * q }
# ↑↑↑ ↑↑↑ ↑↑↑↑↑
You don't want to accumulate orders, you want to accumulate numbers, so you need to supply a number as the starting value; if you don't, the starting value will be the first element, which is an order, not a number
You were mixing up the meaning of the block parameters
You weren't actually summing anything
Now, while inject is capable of summing (in fact, inject is capable of anything, it is a general iteration operation, i.e. anything you could do with a loop, you can also do with inject), it is usually better to use more specialized operations if they exist. In this case, a more specialized operation for summing does exist, and it is called Enumerable#sum:
basket.sum {|item, q| dishes[item] * q }
But there is a deeper underlying problem with your code: Ruby is an object-oriented language. It is not an array-of-hash-of-strings-and-floats-oriented language. You should build objects that represent your domain abstractions:
class Dish < Struct.new(:name, :price)
def to_s; "#{name}: $#{price}" end
def *(num) num * price end
def coerce(other) [other, price] end
end
require 'bigdecimal'
require 'bigdecimal/util'
dishes = {
chicken: Dish.new('Chicken', '12.5'.to_d),
pizza: Dish.new('Pizza', '10'.to_d),
pasta: Dish.new('Pasta', '8.99'.to_d)
}
class Order < Struct.new(:dish, :quantity)
def to_s; "#{quantity} * #{dish}" end
def total; quantity * dish end
end
class Basket
def initialize(*orders)
self.orders = orders
end
def <<(order)
orders << order
end
def to_s; orders.join("\n") end
def total; orders.sum(&:total) end
private
attr_accessor :orders
end
basket = Basket.new(
Order.new(dishes[:pasta], 1),
Order.new(dishes[:pizza], 2)
)
basket.total
#=> 0.2899e2
Now, of course, for such a simple example, this is overkill. But I hope that you can see that despite this being more code, it is also much much simpler. There is no complex navigation of complex nested structures, because a) there are no complex nested structures and b) all the objects know how to take care of themselves, there is never a need to "take apart" an object to examine its parts and run complex calculations on them, because the objects themselves know their own parts and how to run calculations on them.
Note: personally, I do not think that allowing arithmetic operations on Dishes is a good idea. It is more of a "neat hack" that I wanted to show off in this code snippet.
With Ruby 2.4, you could use Hash(Enumerable)#sum with a block :
basket = {"Pasta"=>1, "Pizza"=>2}
prices = {"Chicken"=>12.5, "Pizza"=>10, "Pasta"=>8.99}
basket.sum{ |dish, quantity| quantity * prices[dish] }
# 28.99
Data structure
dishes
dishes (what I called prices to avoid writing dishes[dish]) is the correct data structure :
Hash lookup is fast
If you want to update the price of a dish, you only have to do it in one place
It's basically a mini database.
basket
basket is also fine as a Hash, but only if you don't oder any dish more than once. If you want to order 2 pizzas, 1 pasta and then 3 pizzas again :
{"Pizza"=>2, "Pasta" => 1, "Pizza" =>3}
=> {"Pizza"=>3, "Pasta"=>1}
you'll lose the first order.
In that case, you might want to use an array of pairs (a 2-element array with dish and quantity) :
basket = [["Pizza", 2], ["Pasta", 1], ["Pizza", 3]]
With this structure, you could use the exact same syntax to get the total as with a Hash :
basket.sum{ |dish, quantity| quantity * prices[dish] }
Try this one
basket.inject(0) do |acc, item|
dish, q = item
acc + (dishes[dish] * q)
end
=> 28.990000000000002
one line
basket.inject(0) { |acc, item| acc + (dishes[item.first] * item.last) }
Your variables for the block are wrong. You have the accumulator and an item (that it's an hash)
2.2.0 :011 > basket.inject(0){ |sum, (item, q)| sum + dishes[item].to_f * q }
=> 28.990000000000002

I'm learning Ruby and I don't clearly understand what :* means in inject(:*) [duplicate]

This question already has answers here:
How does this ruby injection magic work?
(3 answers)
Closed 7 years ago.
I'm writing a method for calculating the factorial of a number and I found something similar to this in my search.
def factorial(number)
(1..number).inject(:*) || 1
end
It works and I understand what the inject function is doing, but I don't clearly understand what the (:\*) part really means.
I know it must be a shorthand version of writing {|num, prod| num*prod}, but I would love a clear explanation. Thanks!!
:* is simply the method name for * of the method for inject to execute. If you look at the documentation for inject http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-inject
It states that
If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method.
So taken that inject { |memo, obj| block }
The following are equal
ary = [1,2,3]
ary.inject(:*)
#=> 6
ary.inject { |memo, obj| memo.*(obj) }
#=> 6
Short explanation
:* is a symbol. Symbols are immutable strings. :* is like "*" except it's immutable.
In ruby, multiplication is a method invocation too. It's equivalent invoking the .*(second) method of the first multiplier with the second multiplier as an argument. In fact, you can type 3.*(4) instead of 3*4. 3*4 is just syntactic sugar as far as ruby is concerned.
Method invocation in ruby can be invoked by public_sending symbol messages to objects. 3.public_send(:*, 4) will also work just like 3*4.
The argument to inject is interpreted as what type of message should be public_senT, that is, what method should be invoked from the internals of the inject method.
Longer explanation
You can think of
[ 1, 2, 3, 4 ].inject(:*)
as injecting '*' between each adjacent pair of each enumerable object that inject is invoked on:
[ 1, 2, 3, 4 ].inject(:*) == 1 * 2 * 3 * 4
Of course 1 * 2 * 3 * 4 is equivalent to going from left to right, and applying :* on your running tally and the next number to get your next tally, and then returning the final tally.
module Enumerable
def inject_asterisk
tally = first
rest = slice(1, length - 1)
rest.each do |next_num|
tally = tally * next_num
end
return tally
end
end
[2, 3, 5].inject_asterisk #=> 30
You can generalize this by making the operation that combines the tally and next_number to get your next tally an argument function. Blocks in ruby serve basically as argument functions that always have a reserved spot.
module Enumerable
def inject_block(&block)
tally = first
rest = slice(1, length - 1)
rest.each do |next_num|
tally = block.call(tally, next_num)
end
return tally
end
end
[2, 3, 5].inject_block {|tally, next_num| tally + next_num } #=> 10
If your block is always going to be of the form
{|tally, next_num| tally.method_of_tally(next_num) }
as it is in this case (remember tally + next_num <==> tally.+(next_num) <==> tally.public_send(:+,next_num), you can decide to only pass :method_of_tally as the argument and imply the block.
module Enumerable
def my_inject(method_of_tally_symbol, &block)
if method_of_tally_symbol
block = Proc.new { |tally, next_num|
tally.public_send(method_of_tally_symbol, next_num)
}
end
tally = first
rest = slice(1, length - 1)
rest.each do |next_num|
tally = block.call(tally, next_num)
end
return tally
end
end
[2, 3, 5].my_inject(:+) #=> 10
It's all about extracting repeated patterns into reusable components so that you don't have to type as much.
It means symbol to proc and it's a shortcut. Typically you would write something like
array.map { |e| e.join }
with symbol to proc, the shorthand would be
array.map(&:join)
inject and reduce are similar, but you don't need the & in those cases
For example, if you have an array of numbers called numbers
To sum the numbers, you could do
numbers.inject(&:+)
or you could leave off the ampersand
numbers.inject(:+)
http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-inject
Inject is a method on enumerable that combines the elements of said enumerable using a symbol (as in your case) or a block (as in your proposed longhand).
For example:
(5..10).reduce(:*) is equivalent to (5..10).inject { |prod, n| prod + n }

In Ruby, how can I collect each new element passing through a method into an array?

I'm creating a small prime number program, and am confused about one thing.
I have a function called create_numbers, that generates numbers and passes them to a new function called check_for_primes, which passes only prime numbers to a final function called count_primes. I want to collect each prime into an array in the function count_primes, but for some reason each number is collected as its own array.
Any idea of what I'm doing wrong?
Here is the code:
def create_numbers
nums = 1
while nums < 100
nums = nums + 2
check_for_primes(nums)
end
end
def count_primes(nums)
array = []
array << nums
puts array.inspect
end
def check_for_primes(nums)
(2...nums).each do |i|
if nums%i == 0
nums = false
break
end
end
if nums != false
count_primes(nums)
end
end
create_numbers
Try this:
START = 1
STEP = 2
class Integer
def prime?
return if self < 2
(2...self).each do |i|
return if self % i == 0
end
true
end
end
def create_numbers
num = START
while (num + STEP) < 100
num += STEP
primes << num if num.prime?
end
end
def primes
#primes ||= []
end
create_numbers
p primes
When you want to save the 'state' of something, put it in an instance variable (#var).
It'll be accessible outside of the current function's scope.
Also, try naming your variables differently. For instance, instead of 'nums', in the
create_numbers method, use 'num'. Since the variable is only referencing one number at a
time and not a list of numbers, naming it in the plural will confuse people (me included)...
Hope it helps,
-Luke
each time into count_primes you put a value into array (which should have a better name, btw). Unfortunately, each time it's a new variable called array and since no one outside the function can see that variable it's lost when the function ends. If you want to save the values you've already found you'll need to set some state outside your function.
I can think of 2 quick solutions. One would be to declare your storage at the top of create_numbers and pass it into both functions.
def count_primes(num, arr)
def check_for_primes(nums, arr)
The other would be to set a variable outside all the functions, $array, for example to hold the values.
$array = []
...
$array << num
Since the scope of $array is global (i.e. all functions have access to it) you have access to it from anywhere in the file and can just add things to it in count primes. Note that using globals in this way is generally considered bad style and a more elegant solution would pass parameters and use return values.

ruby and references. Working with fixnums

I know a bit about ruby way to handle objects and references. The replace stuff, ect ...
I know it d'ont work on fixnum, cause the var is the fixnum. But i wish to change the value of a fixnum inside a function, and that the value changed in the ouside var.
How can i do this ?
I guess i can use a string like this "1" but that's quite dirty.
Ruby will always pass-by-reference (because everything is an object) but Fixnum lacks any methods that allow you to mutate the value. See "void foo(int &x) -> Ruby? Passing integers by reference?" for more details.
You can either return a value that you then assign to your variable, like so:
a = 5
def do_something(value)
return 1 #this could be more complicated and depend on the value passed in
end
a = do_something(a)
or you could wrap your value in an object such as a Hash and have it updated that way.
a = {:value => 5}
def do_something(dict)
dict[:value] = 1
end
do_something(a) #now a[:value] is 1 outside the function
Hope this helps.
You could pass an array with a single number, like [1], or a hash like {value: 1}. Less ugly than a string, as your number itself remains a number, but less overhead than a new class...
When I was building a game I had the same problem you have. There was a numeric score that represented how many zombies you've killed and I needed to manually keep it in sync between Player (that incremented the score), ScoreBar and ScoreScreen (that displayed the score). The solution I've found was creating a separate class for the score that will wrap the value and mutate it:
class Score
def initialize(value = 0)
#value = value
end
def increment
#value += 1
end
def to_i
#value
end
def to_s
#value.to_s
end
end

Resources