Ruby Array: Methodology to add numbers [duplicate] - ruby

This question already has answers here:
How to sum array of numbers in Ruby?
(16 answers)
Closed 10 years ago.
I'm trying to write code that will take an array and give back the SUM of the array.
First, is this the correct way to place the numbers into an array? It seems like there may be a problem with that based on the error.
def total(num)
x = []
x << num
puts x.inject(0){|a,b|a+b}
end
Looks like a have a few problems here. First, I get this error when I call the method with sum([3,2,41,2]):
`total': wrong number of arguments (5 for 1) (ArgumentError) from calculator.rb:11
I also recall getting a error: cant't covert fixnum into array

Your inject block is correct. Your argument error arises because you defined the method to take a single argument, but in your example, you call it with four arguments. If you want to use a variable number of arguments, you can use the splat operator *, which does various things- in this case, it will gather all undefined arguments into an array:
def total(*nums)
nums.inject(0) {|a,b| a + b }
end
total(3,2,41,2) #=> 48
You can further simplify this using a symbol with inject:
nums.inject(0, :+) #=> 48
This works by sending the method denoted by the symbol to the accumulator, using each member of the array as an argument (equivalent to defining the block as {|a, b| a.send(:+, b) }).
And actually in this case, you don't need to define an initial value. Inject has a third form that will simply use the first member of the array as the initial value and sum the others onto it:
nums.inject(:+)

Related

Forwarding/delegating 2 arguments in ruby

class MyClass
extend Forwardable
def_delegators :#broker, :add
def subscribe_in_broker
#subscribers.map(&method(:add))
end
end
In this example #subscribers is a Hash and #broker.add takes two arguments: def broker(k,v).
This causes ArgumentError: wrong number of arguments (given 1, expected 2)
Is there a way to use Forwardable or a similar solution without this problem? How can I easily delegate an Array with 2 elements to a method which takes two arguments? What would be the equivalent of writing:
def subscribe_in_broker
#subscribers.map{|k,v| add(k,v) }
end
but using delegation?
It might seem like the block is receiving 2 arguments, but it is actually receiving only a single argument (a 2-element array). Ruby is interpreting the |x, y| as if it was |(x, y)|. The parentheses (which can be omitted here) destructure the single argument into its two pieces.
When you use a & with a method, it also only gets only 1 argument, and if you want to destructure it, you have to be explicit by using a set of parentheses around x and y:
def add((x,y))
end
To be clear, this is a single-argument method that expects the argument to be an array. For example, you call it like this: add([1, 2])
Note: this has nothing to do with delegation, just the fact that Ruby assumes you intend to destructure in one place, while not in another.
It is possible to yield more than one argument to a block (or a method turned into a proc with '&'), but that is not what map does.
It's not obvious to me why hash.map { |x,y| add(x, y) } is not adequate for your purposes. To work around that, you'd need to define your own map function that yields 2 arguments instead of 1, and that seems like overkill for this.

Using symbols to reference blocks in Ruby e.g. :+, :-, :* [duplicate]

This question already has answers here:
What are :+ and &:+ in Ruby?
(2 answers)
Closed 7 years ago.
I'm learning about the use of symbols in Ruby and have realized they act largely as references to variables, keys in hash tables, and even as a way of sending blocks in methods.
My questions is, what exactly are symbols, such as :+ :- :*, referencing when I use them in a method?
e.g. using :+ to sum all the values in an array:
puts [1,2,3].reduce(:+)
=> 6
gives the same result as:
puts [1,2,3].reduce {|sum, i| sum += i}
=> 6
and if I create my own version of :+
a = lambda {|sum,i| sum += i}
puts [1,2,3].reduce(&a)
=> 6
My first thought is therefore that :+ references {|sum, i| sum += i} as an explicit block but I've had trouble finding information to confirm that.
The symbol you pass to reduce will be interpreted as a name of a method to call on each element. So this
collection.reduce(:foo)
is equivalent to this
collection.reduce { |memo, element| memo.foo(element) }
The reason it works with sums is that + operator is actually just a method on numbers.
1.+(3) # => 4
My first thought is therefore that :+ references {|sum, i| sum += i} as an explicit block
Not sure what you mean there, but :+ most certainly does not reference this block. Or any block. Or anything.
Symbols are just names. They don't point to anything. Deciding what they mean is up to the code that uses them.
A symbol is not a reference to variables, keys in hash tables, or as a way of sending blocks as you claim. The truth is that a symbol is used to describe these things as designed by the person who wrote the respective method that uses them, and actual mapping of a symbol to these things is done within the respective method.
For you particular example, :+ is not referencing {|sum, i| sum += i}, or any other block; it is a peculiarity of the alternative syntax of reduce that allows a symbol to be passed, and converts that symbol to a block. The corresponding block, though close to it, it not what you thought, but is: {|sum, i| sum + i}.

The difference between :+ and &:+ [duplicate]

This question already has answers here:
What are :+ and &:+ in Ruby?
(2 answers)
Closed 8 years ago.
I have code like this
list << num if num.to_s.split("").map(&:to_i).map(&:factorial).inject(:+) == num
It works, and I was wondering how inject works without the & (ampersand) in front of the :+. I am asking for someone to explain what the differences are between :+ and &:+.
&:+ is translated to a proc, while :+ is a Symbol. inject supports receiving symbols, which is translated internally to a proc:
If you specify a block, then for each element in enum the block is
passed an accumulator value (memo) and the element. 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.

What is the purpose of |element| in Ruby array operations syntax?

[1,2,3,4,5,6,7].delete_if{|i| i < 4 }
For example, in the above, why do you need to put |i| before i < 4?
I'm new to Ruby programming and the purpose of this element escapes me.
This is very basic Ruby syntax for a block. A block can sometimes take parameters which are given between the bars |. In your case:
[1,2,3,4,5,6,7].delete_if { |i| i < 4 }
The delete_if method for the type Array accepts a block as a parameter. When the bock is given, the block accepts the array element as a parameter. So it iterates i over each value within the array in this case. More specifically, an element will be deleted from the array if that element is < 4. The result will be:
[4,5,6,7]
You'll often see documentation for methods for Ruby types which say, for example:
delete_if { |item| block } -> Array
Which means that the method accepts a block with a parameter, the block being some code that uses the parameter, and the output being another array. The method's description explains more detail in the documentation (e.g., Ruby Array).
I recommend reading some Ruby getting started information online or a good introductory book which will explain this in more detail.
You have to put i there for the same reason you would put i in the first line here:
def plus_one(i)
return i + 1
end
You have to name your method argument, which you later use as a local variable in the method.
Ruby blocks are similar to methods, they can also receive arguments, and syntax for declaring them is slightly different: enclosing them in | |.
I've redone my answer, even though the OP's question has already been answered, because I thought of a new way to explain this that may help future SO users with the same question.
From high school algebra, you should remember functions like this: f(x) = x + 1.
Imagine putting curly braces around the x + 1: f(x) = { x + 1 }
Then move the (x) to inside the curly braces: f = {(x) x + 1 }
And then get rid of the name f: {(x) x + 1 }. This makes it an "anonymous function," i.e. a "lambda."
Here's the problem: The braces could contain arbitrary statements, which may themselves use parentheses: (x + 1) * 4. So how would Ruby know that the (x) is supposed to be an argument to the function, and not an expression to execute? Some other syntax had to be used. Hence the vertical bars: |x|. (At least I assume that was the thought process).
So {|i| i > 4 } is just like f(i) = i > 4, except that it has no name and is not defined in advance, so the parameter has to be defined "inside" the function itself, rather than being outside attached to the name.
Array#delete_if expects such a function (called a "block" when it's used like this) and knows what to do with it. It passes each member of the array [1,2,3,4,5,6,7] into the block as the argument i to see whether i > 4 is true for that member. It's equivalent to doing something like this:
def greater_than_four(x)
x > 4
end
arr = [1,2,3,4,5,6,7]
arr.each do |el|
arr.delete(el) if greater_than_four(el)
end
You could avoid defining the greater_than_four method in advance by defining a lambda on the fly like this:
arr = [1,2,3,4,5,6,7]
arr.each do |el|
arr.delete(el) if lambda{|i| i > 4}.call(el)
end
But since Array#delete_if already expects a block, and already knows to call it on each element, you can save yourself a whole lot of code:
[1,2,3,4,5,6,7].delete_if{|i| i < 4 }
The parameter which you are passing to the delete_if method is a block and the thing inside the parameter you pass to the block.
Think of the block as a method of sorts. The delete_if method iterates over the block and passes the current item as the parameter i to the block. If the condition evaluates to true then that element gets deleted.

How to pass a function instead of a block [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Shorter way to pass every element of an array to a function
I know this will work:
def inc(a)
a+1
end
[1,2,3].map{|a| inc a}
but in Python, I just need to write:
map(inc, [1,2,3])
or
[inc(x) for x in [1,2,3])
I was wondering whether I can skip the steps of making a block in Ruby, and did this:
[1,2,3].map inc
# => ArgumentError: wrong number of arguments (0 for 1)
# from (irb):19:in `inc'
Does anyone have ideas about how to do this?
According to "Passing Methods like Blocks in Ruby", you can pass a method as a block like so:
p [1,2,3].map(&method(:inc))
Don't know if that's much better than rolling your own block, honestly.
If your method is defined on the class of the objects you're using, you could do this:
# Adding inc to the Integer class in order to relate to the original post.
class Integer
def inc
self + 1
end
end
p [1,2,3].map(&:inc)
In that case, Ruby will interpret the symbol as an instance method name and attempt to call the method on that object.
The reason you can pass a function name as a first-class object in Python, but not in Ruby, is because Ruby allows you to call a method with zero arguments without parentheses. Python's grammar, since it requires the parentheses, prevents any possible ambiguity between passing in a function name and calling a function with no arguments.
Does not answer your question but if you really just want to increment all your variables, you have Integer#next
4.next
#=> 5
[1,2,3].map(&:next)
#=> [2, 3, 4]

Resources