Why can't I print the result of reduce/inject directly [duplicate] - ruby

This question already has answers here:
Code block passed to each works with brackets but not with 'do'-'end' (ruby)
(3 answers)
Closed 4 years ago.
Found an interesting quirk in the ruby interpreter - at least in MRI 2.4.2.
As far as I can tell, each of the below code snippets should print '123'. If I try to print the result of reduce directly, I get NoMethodError: undefined method '' for 1:Integer (or whatever type the array contains) But if I first save the result and then print it, it works fine..
So, this code is broken:
puts [1,2,3].reduce('') do |memo, num|
memo + num.to_s
end
And this code works:
temp = [1,2,3].reduce('') do |memo, num|
memo + num.to_s
end
puts temp
These should work exactly the same, right? Should this be filed as a bug? Am I just missing something fundamental here?
I'd think it should at least show which method is trying to be called. Can't find anything in google about an undefined method without a method name...

You need parenthesis on the puts call. This works:
puts([1,2,3].reduce('') do |memo, num|
memo + num.to_s
end)
Normally you can avoid parenthesis, but sometimes the parser will find ambiguities and will just raise an error, like in your first case.

Related

Why doesn't my ruby method call work? (yield) [duplicate]

This question already has answers here:
Passing block into a method - Ruby [duplicate]
(2 answers)
Closed 2 years ago.
I can't figure out why I get this error message when I run my file on the console: no block given (yield) (LocalJumpError)
Here my code:
def block_splitter(array)
array.partition { |item| yield(item) }
end
beatles = ["John", "Paul", "Ringo", "George"]
puts block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end
Thanks for your help!
It's a whitespace issue. Your problem is in this line:
puts block_splitter(beatles) do |beatle|
# ...
end
The above code is being interpreted like this:
puts(block_splitter(beatles)) do |beatle|
# ...
end
I.e. the ruby interpreter thinks that the block is being passed to the puts method, not the block_splitter method.
By assigning a variable and printing the result, you'll see that this works as expected:
result = block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end
puts result
Or, you can define this as a 1-liner, and the ruby interpreter handles it like you expected:
puts block_splitter(beatles) { |beatle| beatle.start_with?("P") }
Or, you could wrap it in extra brackets:
puts(block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end)
So there is a problem with missing parentheses. Ruby interpreter allow not to use those, but when You use nested method calls It's better (and sometimes necessary) to use them. To fix it You can do something like this
puts(block_splitter(beatles) do |beatle|
beatle.start_with?("P")
end)
Or even better
puts(block_splitter(beatles) {|beatle| beatle.start_with?("P")})

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

Function overloading in Ruby [duplicate]

This question already has answers here:
Why doesn't ruby support method overloading?
(9 answers)
Closed 7 years ago.
I have this piece of code in Ruby.
class Superheros
class<<self
def foo1(param1)
print "foo1 got executed\n"
end
def foo1
print "foo1 without param got executed\n"
end
def foo3(param1,param2)
print "foo3 got executed\n"
end
end
end
print Superheros.foo3(2,3)
print Superheros.foo1
print Superheros.foo1
print Superheros.foo1(5)
I get the error in Superheros.foo1(5) . But i already have the function foo1(param1) to match it with, but it gives me an error
`foo1': wrong number of arguments (1 for 0) (ArgumentError)
Why is that?
PS: I found out if i remove the function name without the parameter,
the Superheros.foo1(5) works just fine.
Ruby doesn't support method overloading. In your code, your second definition of foo1 replaced the first. That's why you get an error when you try to pass arguments, the method that accepted arguments is gone.
There is a question on SO about this topic here, with some good explanations.
Maybe you could use variable arguments:
def foo1(*args)
case args.length
when 1
puts "Function A"
when 2
puts "Function B"
else
puts "Called with #{args.length} arguments"
end
end

Ruby hash/sub-hash existance check [duplicate]

This question already has answers here:
How to avoid NoMethodError for missing elements in nested hashes, without repeated nil checks?
(16 answers)
Closed 7 years ago.
I find myself needing to put guards like this:
if hash[:foo] && hash[:foo][:bar] && hash[:foo][:bar][:baz]
puts hash[:foo][:bar][:baz]
end
I'd like to shorten this in some way; I know I can wrap in a begin/rescue block but that seems worse. Maybe something like:
ruby Hash include another hash, deep check
Something like:
def follow_hash(hash, path)
path.inject(hash) { |accum, el| accum && accum[el] }
end
value = follow_hash(hash, [:foo, :bar, :baz])
puts value if value
I found this article very informative: http://avdi.org/devblog/2011/06/28/do-or-do-not-there-is-no-try/
value = Maybe(params)[:foo][:bar][:baz][:buz]

Resources