I'm trying to learn Ruby and I came across a problem online where I need to create something similar to this :
add(1) # => 1
add(1).(2) # => 3
add(1).(2).(3) # => 6
and so on.
I've read around and the closest I can get to having this work is with the following code :
def add(n)
Proc.new { |s| s + n }
end
Which works to an extent.
I can run the following :
add(1) # => #<Proc:0x007fb8aac309f0#(pry):2>
# or
add(1).(2) # => 3
But the moment I try doing this :
add(1).(2).(3)
I get the following error :
NoMethodError: undefined method call for 3:Integer
Why am I getting this error and how would I change this method so that it will accept as many new values as I give it to add itself without giving me an error?
For reference, this problem comes from CodeWars "A Chain Adding Function".
The easiest way to achieve this is to define Integer#call:
class Integer
def call(n)
self + n
end
end
def add(n)
n
end
puts add(1)
# 1
puts add(1).(2)
# 3
puts add(1).(2).(3)
# 6
The returned objects are plain Ruby Integers, not Proxy objects.
Note that it's probably not a good idea to modify such an important class. It's possible to use refinements in order to avoid breaking other Ruby parts:
module ChainAdding
refine Integer do
def call(n)
self + n
end
end
end
def add(n)
n
end
using ChainAdding
puts add(1)
# 1
puts add(1).(2)
# 3
puts add(1).(2).(3)
# 6
If you're learning Ruby, this kind of metaprogramming trick isn't what you should concentrate on first. It's cool to know that we can do this, it doesn't mean that we should, though.
You can't quite do what you are trying to do. The reason is that you have to return something and that something can't change based on whether you are going to continue the chain or not.
Note that some Rails objects give you the illusion that you can in the REPL (irb, pry), but they actually just redefine #inspect, which is called upon visualisation by said REPL.
Instead you can do something very similar, which has an even better interface IMO:
def add(starting_number)
sum = starting_number
accumulator = proc do |next_number|
if next_number
sum += next_number
accumulator
else
sum
end
end
end
add(1) # => #<Proc:0x007fb8ae20e860#(pry):4>
add(1).(2) # => #<Proc:0x007fb8ae4894f0#(pry):4>
add(1).() # => 1
add(1).(2).() # => 3
add(1).(2).(3).(4).() # => 10
Related
I've been given an interesting ruby question for a technical test. I did not find an answer in the time given, but i think it was interesting and would like someone else's take on this. How would you write code to do:
2.times.each_second { p 'works' }
I tried extending the enumerator class, with a per_second method. The per_second method just sleeps for the correct number of milliseconds. But i was stuck trying to pass the "2" argument to the per_second method in an elegant fashion. Maybe using the length of the enumerator returned from 2.times?
Using the magic of modern Ruby, we can write a refinement to Integer that augments the times method, but only in scopes that opt into this refinement behavior.
module IntegerExt
# Define a refinement of the Integer type. This is like
# monkeypatching but is local to modules or files that opt in to the
# change.
refine Integer do
# Re-write the times function.
def times(&block)
if block
# If a block was provided, then do the "normal" thing.
super(&block)
else
# Otherwise, get an enumerator and augment its singleton class
# with PerSecondEnumerator.
super().extend PerSecondEnumerator
end
end
end
# This module provides the per_second method on enumerators that
# need it.
module PerSecondEnumerator
def per_second(&block)
loop do
sleep(1.0/self.size)
block.call
end
end
end
end
# If a module wishes to have this functionality, it needs to opt in
# with this 'using' statement. The per_second syntax only works on
# objects constructed within the lexical scope of this refinement.
using IntegerExt
puts 2.times.inspect # Still works
2.times.per_second { p 'works' }
This deals with a couple of the concerns of Aria's answer. We're no longer globally monkey-patching a built-in class. We only modify the class in the lexical scope of any module that wishes to see our changes. Further, since we've refined times rather than Enumerator directly, our per_second only works on enumerators constructed via times, so it's impossible to do something nonsensical like [1, 2, 3, 4, 5].per_second { ... }.
You could do it like that:
Enumerator.define_method(:per_second) do |&block|
loop do
sleep(1.0/self.size)
block.call
end
end
3.times.per_second { puts "works" }
But here are some warnings:
It's not recommended to expand a Ruby base class like Enumerator, Integer, etc.
Like someone said in a comment on your post, it's not good to use size, since you can use a string or array instead of a integer.
Here is another option using a simple object
obj = Object.new
def obj.each_second(times: 1, duration: Float::INFINITY)
return to_enum(__method__, times: times, duration: duration) unless block_given?
i = 0
until i >= duration * times do
i += 1
yield(i)
sleep(1.0/times)
end
end
enum = obj.each_second(times: 5, duration: 3)
enum.each { |n| puts "#{n} Works"}
# 1 Works
# 2 Works
# 3 Works
# 4 Works
# 5 Works
# 6 Works
# 7 Works
# 8 Works
# 9 Works
# 10 Works
# 11 Works
# 12 Works
# 13 Works
# 14 Works
# 15 Works
#=> nil
You can specify the number of times to execute the block per second and the duration of the execution in seconds. (Defaults to 1 iteration per second indefinitely)
Note: This does not take into account the actual blocking execution time of the block itself so the number of iterations per second is not guaranteed and is likely to be less than the specified number.
still new to Ruby. Noob question, please bear with me. And thank you ahead of time.
I love the .each method over something like the while loop. The while loop is confusing for me. I love the neatness of the .each method. Can I use the .each method to solve this question below? I'm pulling my hair out trying to figure out if I can use this style loop.
Thank you for your help!
def sum_nums(max)
puts sum_nums(4) # => 10, because 1 + 2 + 3 + 4 = 10
puts sum_nums(5) # => 15
Yes , you can use the .each method to solve this question. Pulling hairs out is not the way however. Try initializing a variable to 0 outside the each- block, and add all values in the block to that variable.
So to answer the OP (and borrowing shamelessly from all the other answers)
def sum_nums(n)
(1..n).sum
end
puts sum_nums(5)
# => 15
Here are a few options of how to sum all numbers from 1 to n in Ruby:
1.upto(4).reduce(:+) # => 10
1.upto(5).reduce(:+) # => 15
(1..4).reduce(:+) # => 10
(1..5).reduce(:+) # => 15
# could use `each`, but it is doing nothing here.
(1..4).each.reduce(:+) # => 10
(1..5).each.reduce(:+) # => 15
# same thing with `each`, but keeping track of the sum manually
sum = 0
(1..4).each do |i|
sum += i
end
# i => 10
Basically you get an Enumerable (iterator/collection abstraction) that spans over the things you would like enumerate. In our case we would like to go from 1 to 4. We can get an Enumerable for that either by calling Integer#upto method (like so: 1.upto(4), or by using Range (like so (1..4)). Then we can use any Enumerable methods on the collection/iterator.
Enumerable#each simply calls a block for every element (and when called without a block, essentially does nothing, just returns the same Enumerable), but Enumerable also has built-in functions for finding minimum, maximum, reducing (in our case we're doing the sum, so reduce fits), etc. For more detailed information see docs for Enumerable.
An example way that comes into my mind.
def sum_nums(max)
sum = 0
array = Array.new(max) { |i| (i + 1) }
array.each { |element| sum += element }
puts sum
end
sum_nums(4) # => 10
sum_nums(5) # => 15
Can anyone help me to figure out the the use of yield and return in Ruby. I'm a Ruby beginner, so simple examples are highly appreciated.
Thank you in advance!
The return statement works the same way that it works on other similar programming languages, it just returns from the method it is used on.
You can skip the call to return, since all methods in ruby always return the last statement. So you might find method like this:
def method
"hey there"
end
That's actually the same as doing something like:
def method
return "hey there"
end
The yield on the other hand, excecutes the block given as a parameter to the method. So you can have a method like this:
def method
puts "do somthing..."
yield
end
And then use it like this:
method do
puts "doing something"
end
The result of that, would be printing on screen the following 2 lines:
"do somthing..."
"doing something"
Hope that clears it up a bit. For more info on blocks, you can check out this link.
yield is used to call the block associated with the method. You do this by placing the block (basically just code in curly braces) after the method and its parameters, like so:
[1, 2, 3].each {|elem| puts elem}
return exits from the current method, and uses its "argument" as the return value, like so:
def hello
return :hello if some_test
puts "If it some_test returns false, then this message will be printed."
end
But note that you don't have to use the return keyword in any methods; Ruby will return the last statement evaluated if it encounters no returns. Thus these two are equivelent:
def explicit_return
# ...
return true
end
def implicit_return
# ...
true
end
Here's an example for yield:
# A simple iterator that operates on an array
def each_in(ary)
i = 0
until i >= ary.size
# Calls the block associated with this method and sends the arguments as block parameters.
# Automatically raises LocalJumpError if there is no block, so to make it safe, you can use block_given?
yield(ary[i])
i += 1
end
end
# Reverses an array
result = [] # This block is "tied" to the method
# | | |
# v v v
each_in([:duck, :duck, :duck, :GOOSE]) {|elem| result.insert(0, elem)}
result # => [:GOOSE, :duck, :duck, :duck]
And an example for return, which I will use to implement a method to see if a number is happy:
class Numeric
# Not the real meat of the program
def sum_of_squares
(to_s.split("").collect {|s| s.to_i ** 2}).inject(0) {|sum, i| sum + i}
end
def happy?(cache=[])
# If the number reaches 1, then it is happy.
return true if self == 1
# Can't be happy because we're starting to loop
return false if cache.include?(self)
# Ask the next number if it's happy, with self added to the list of seen numbers
# You don't actually need the return (it works without it); I just add it for symmetry
return sum_of_squares.happy?(cache << self)
end
end
24.happy? # => false
19.happy? # => true
2.happy? # => false
1.happy? # => true
# ... and so on ...
Hope this helps! :)
def cool
return yield
end
p cool {"yes!"}
The yield keyword instructs Ruby to execute the code in the block. In this example, the block returns the string "yes!". An explicit return statement was used in the cool() method, but this could have been implicit as well.
I'm attempting to add conversion methods to the Numeric class but when I run the following lines of code I get a SystemStackError
puts 5.dollars.in(:euros) # => 6.5
puts 1.dollar.in(:yen)
Here is my Numeric class
class Numeric
##conversion_hash = {:dollar => {:yen => 0.013, :euros => 1.292, :rupees => 0.019}}
def method_missing(method_id)
name = method_id.to_s
if name =~ /^dollar|yen|euros|rupee|$/
self.send(name + 's')
else
super # pass the buck to superclass
end
end
def dollars()
puts "Called Dollars method"
#current_currency = :dollar
return self
end
def in(key)
if ##conversion_hash.has_key?(#current_currency)
puts "Current currency: #{#current_currency}"
conversion_rate = ##conversion_hash[#current_currency]
puts "Current conversion rate: #{conversion_rate}"
if conversion_rate.has_key?(key)
puts "we have that key"
puts"What am I? #{self}"
rate = conversion_rate[key]
puts "Rate to multiply by #{rate}"
return self.to_int * conversion_rate[key]
end
end
end
end
Any help is greatly appreciated.
You're getting infinite recursion in your method_missing because your regex isn't quite right. Try changing the line:
if name =~ /^dollar|yen|euros|rupee|$/
to:
if name =~ /^dollar|yen|euros|rupee$/
That extra | is causing anything to match the regex, so any other method is recursing with a continually extending suffix of s.
In this case it looks like puts seems to be trying to call to_ary when it's trying to determine the type its argument. I'm not exactly sure why it's not just using respond_to? though - it's deep in the C internals so I don't really know what's happening.
Your solution overcomplicated.
- You don't need to modify method_missing. Armando version works fine.
- You should simple dollar definition to hash plus
- find the way to call method_missing again from method in(this is your homework).
Working solution have only 1 line of code + 2 lines def surronding.
In Ruby:
for i in A do
# some code
end
is the same as:
A.each do |i|
# some code
end
for is not a kernel method:
What exactly is "for" in ruby
Is there a way to use other keywords to do similar things?
Something like:
total = sum i in I {x[i]}
mapping to:
total = I.sum {|i] x[i]}
It's almost syntax sugar. One difference is that, while for would use the scope of the code around it, each creates a separate scope within its block. Compare the following:
for i in (1..3)
x = i
end
p x # => 3
versus
(1..3).each do |i|
x = i
end
p x # => undefined local variable or method `x' for main:Object
for is just syntax sugar for the each method. This can be seen by running this code:
for i in 1 do
end
This results in the error:
NoMethodError: undefined method `each' for 1:Fixnum
For is just syntactic sugar.
From the pickaxe:
For ... In
Earlier we said that the only built-in Ruby looping primitives were while and until. What's this ``for'' thing, then? Well, for is almost a lump of syntactic sugar. When you write
for aSong in songList
aSong.play
end
Ruby translates it into something like:
songList.each do |aSong|
aSong.play
end
The only difference between the for loop and the each form is the scope of local variables that are defined in the body. This is discussed on page 87.
You can use for to iterate over any object that responds to the method each, such as an Array or a Range.
for i in ['fee', 'fi', 'fo', 'fum']
print i, " "
end
for i in 1..3
print i, " "
end
for i in File.open("ordinal").find_all { |l| l =~ /d$/}
print i.chomp, " "
end
produces:
fee fi fo fum 1 2 3 second third
As long as your class defines a sensible each method, you can use a for loop to traverse it.
class Periods
def each
yield "Classical"
yield "Jazz"
yield "Rock"
end
end
periods = Periods.new
for genre in periods
print genre, " "
end
produces:
Classical Jazz Rock
Ruby doesn't have other keywords for list comprehensions (like the sum example you made above). for isn't a terribly popular keyword, and the method syntax ( arr.each {} ) is generally preferred.