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

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.

Related

what is the difference between .capitalize and .capitalize!(or .map & .map!...etc.) and in ruby [duplicate]

This question already has answers here:
What is the purpose of "!" and "?" at the end of method names?
(5 answers)
Closed 6 years ago.
learning how to code with Ruby and was trying learn from test first.
and I stumbled something funny.
I was trying to capitalize every word but
title = 'stuart little'
a = title.split
a.each do |x|
x.capitalize
end
a.join(' ')
This one's result is 'stuart little'
but if I add the ! in capitalize
title = 'stuart little'
a = title.split
a.each do |x|
x.capitalize!
end
a.join(' ')
it ends up with the result I want which is 'Stuart Little'
just .capitalize should work shouldn't it? since I'm just capitalizing the words. and what makes .capitalize! work in this scenario?
When a method has a ! at the end in Ruby, it is commonly referred to as a bang-method. The exclamation point indicates that the method is the dangerous version of another method.
In this case, capitalize! will modify your string, while capitalize will return a new string object. Since you are later calling on your original objects (the strings in a), your code will only work with capitalize!. To make the code work with capitalize, you would have to set that index of the array to the result of the method, e.g. a[index] = x.capitalize
if you really want to learn I like to go to the source
for map for map!. the source would tell you what the difference is
map- Invokes the given block once for each element of self.
and
map! - Invokes the given block once for each element of self,
replacing the element with the value returned by the block.

in ruby, when is a block not a block? [duplicate]

This question already has an answer here:
Ruby block and unparenthesized arguments
(1 answer)
Closed 7 years ago.
Jokes aside, I have a strange situation, I have some code:
def remotes(form,remotes)
personalised_form = form.dup
remotes.each do |ident,remote|
object = yield(ident)
result = remote.call(object)
insert_into_(personalised_form,ident,result)
end
personalised_form
end
And I'm seeing if it works like so:
pp remotes(forms,remotes) do |ident|
case(ident)
when :get_assets
'#Userobject'
end
end
The problem is that ruby seems to think I'm not passing a block to the remotes function.
Why is ruby insisting that I'm not passing a block? (it gives a no block given (yield) (LocalJumpError) specifically).
Thought it's not relevant, remotes is a hash containing key's and Procs, and form is just a specificly structured hash that has the result of the proc inserted into it using the ident to locate the correct insertion point
Ruby thinks you are passing the block to pp method, which simply ignores it. Try:
res = remotes(forms,remotes) do |ident|
case(ident)
when :get_assets
'#Userobject'
end
end
pp res

What about implicit yield in Ruby? [duplicate]

This question already has answers here:
Can you supply arguments to the map(&:method) syntax in Ruby?
(9 answers)
Closed 8 years ago.
I often write:
some_array.each { |array_element| array_element.some_method(args) }
Why not have an option for an implicit yield so you could write e.g.:
some_array.each { _.some_method(args) }
I am not sure what character _ should actually be, and I imagine it would only be used in the most boilerplate setting, where you're dealing with a one-dimensional array and just trying to yield each item to the block in succession. It would save a lot of redundant typing.
Are you familiar with #to_proc and the & syntax for method calls? They cover some cases similar to the one you show here. For example:
[1, -2, -4].map(&:abs) => [1, 2, 4]
The & is used to pass an object in place of a block. If the object isn't a Proc, #to_proc is automatically called on it to get a Proc before it is used in place of the block. Symbol#to_proc returns a Proc which behaves like: { |obj, *args| obj.symbol_name(*args) }.
In your example, you use args which are presumably captured from the surrounding lexical environment. Symbol#to_proc won't help you there. But it wouldn't be hard to make a new Proc-building method which would. For example:
class Symbol
def with_args(*args)
Proc.new { |x| x.send(self, *args) }
end
end
Then you could do:
some_array.each(&:some_method.with_args(args))
Whether that is any better than an explicit block is up to you. In any case, this is a technique you should be aware of.

Ruby Array: Methodology to add numbers [duplicate]

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(:+)

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