I want this code to define two methods, nilguard and falseguard, which guard against nil and false values.
Object.class_eval do
#list = [false, nil]
#list.each do |i|
define_method :"#{i}guard" do |other|
if self == i
return other
else
return self
end
end
end
end
For some reason, it only defines falseguard, which works fine. Why is this, why isn't it defining the other method?
nil.to_s == '', so your second method will just be called guard. You might want to use something like this instead:
#list = { false: false, nil: nil }
#list.each do |s, i|
define_method "#{s}guard" do |other|
# ...
Related
I was implementing my own enumerable method using Ruby when I reach my_any? method. It doesn't pass some cases
my_any? works for some of the cases but others don't work
module Enumerable
def my_each
i = 0
while i < size
yield self[i]
i += 1
end
end
def my_any?
if block_given?
my_each {|item| return true if yield item}
end
false
end
end
The below test cases are the require output
Test case 1
[4,8,true,18].my_any? # => true
test case 2
[2,nil,5,true].my_any? # => true
test case 3
[nil,nil,nil,false].my_any? # => false
test case 4
[nil,nil,nil].my_any? # => false # But my_any? fails this condition
Any Idea please
def my_any?
if block_given?
my_each {|item| return true if yield item}
end
false
end
This method is immediately checking if it was called with a block and, if not, returns false. For example, [].my_any? will return false for block_given? while [].my_any? { true } will return true. None of your examples are passing a block, so they will always skip the if and return false.
The correct behavior of any? should be this: if a block is given, yield the value to it to determine if it satisfies the condition; if a block is not given, just check if the value is truthy.
def my_any?
my_each do |item|
if block_given?
return true if yield item
else
return true if item
end
end
false
end
or more succinctly
def my_any?
my_each {|item| return true if block_given? ? yield(item) : item }
false
end
I often memoize Ruby methods using the begin ... end block syntax:
$memo = {}
def calculate(something)
$memo[something] ||= begin
perform_calculation(something)
end
end
However, there's a gotcha here. If I return early from the begin ... end block via a guard clause, the result is not memoized:
$memo = {}
def calculate(something)
$memo[something] ||= begin
return 'foo' if something == 'bar'
perform_calculation(something)
end
end
# does not memoize 'bar'; method will be run each time
I can avoid this by avoiding the return statement:
$memo = {}
def calculate(something)
$memo[something] ||= begin
if something == 'bar'
'foo'
else
perform_calculation(something)
end
end
end
This works, but I don't love it because:
it's easy to forget I'm not allowed to use return in this case.
with many conditions it clutters up the code as opposed to the guard clause.
Is there a better idiom for this besides just avoiding return?
As far as I know, begin...end cannot be short-circuited. You can do exactly what you're trying to do with procs though:
$memo = {}
def calculate(something)
$memo[something] ||= -> do
return 'foo' if something == 'bar'
perform_calculation(something)
end.call
end
That being said, I've never seen this done before, so it's certainly not idiomatic.
I'd add another layer:
def calculate(something)
$memo[something] ||= _calculate(something)
end
def _calculate(something)
return if something == 'bar'
perform_calculation(something) # or maybe inline this then
end
This has the additional benefit of providing you with a method you could call whenever you want to be sure to get a freshly computed result. I would spend some more time on the method naming though.
One way to tackle this is with meta-programming where you wrap the method after it's defined. This preserves any behaviour in it:
def memoize(method_name)
implementation = method(method_name)
cache = Hash.new do |h, k|
h[k] = implementation.call(*k)
end
define_method(method_name) do |*args|
cache[args]
end
end
This creates a closure variable which acts as a cache. That avoids the ugly global, but it also means you can't really clear out that cache if you need to, so if you pass in a large number of different arguments it could end up consuming a lot of memory. Be cautious! That functionality could be added if necessary by defining some auxiliary method like x_forget for any given method x.
Here's how it works:
def calculate(n)
return n if (n < 1)
n + 2
end
memoize(:calculate)
Then you can see:
10.times do |i|
p '%d=%d' % [ i % 5, calculate(i % 5) ]
end
# => "0=0"
# => "1=3"
# => "2=4"
# => "3=5"
# => "4=6"
# => "0=0"
# => "1=3"
# => "2=4"
# => "3=5"
# => "4=6"
I fear I don't understand the question correctly, as it would seem something quite simple would do.
$memo = {}
def calculate(something)
$memo[something] ||= something == 'bar' ? 'foo' : perform_calculation(something)
end
Let's try it.
def perform_calculation(something)
'baz'
end
calculate('bar')
#=> "foo"
$memo
#=> {"bar"=>"foo"}
calculate('baz')
#=> "baz"
$memo
#=> {"bar"=>"foo", "baz"=>"baz"}
calculate('bar')
#=> "foo"
$memo
#=> {"bar"=>"foo", "baz"=>"baz"}
I don't know a solution using return but for the guard clause in your example I would use case.
$memo = {}
def calculate(something)
$memo[something] ||= case something
when 'foo' then 'bar'
else perform_calculation(something)
end
end
You could use break statements in a #tap block:
def val
#val ||= default.tap do
break val1 if cond1
break val2 if cond2
break val3 if cond3
end
end
You could also use 1.6's #then or 1.5's #yield_self, but then don't forget to return the default value at the end of the block, or it'll default to nil. In your example it's not a problem since the default is only evaluated at the end:
def calculate(something)
#calculate ||= {}
return #calculate[something] if #calculate.key?(something)
#calculate[something] = something.then do |something|
break 'foo' if something == 'bar'
perform_calculation(something)
end
end
A chained method should only be called under certain circumstances in the following code.
class Klass
def foo
puts 'foo'
self
end
def bar
puts 'bar'
self
end
end
klass = Klass.new
a = 2
id = klass.foo{conditionally chain bar if a == 2}.bar
Can you insert an expression or method between chained methods that conditionally continues or halts the method chain?
This is simple and who will come after you will understand immediately:
klass = klass.foo
klass = klass.bar if a == 2
etc...
This works well if the chained methods take no arguments
klass.define_singleton_method :chain_if do |b, *s|
return unless b
klass = self
s.each do |x|
klass = klass.send x
end
klass
end
klass.foo.chain_if(true, :foo, :bar).chain_if(false, :bar)
Here some duplicated threads!
conditional chaining in ruby
Add method to an instanced object
Here I found another solution that I personally like:
my_object.tap{|o|o.method_a if a}.tap{|o|o.method_b if b}.tap{|o|o.method_c if c}
EDIT:
beware tap is defined as follows:
class Object
def tap
yield self
self
end
end
What you need might look like this, if the chained method returns a new immutable object:
class Object
def tap_and_chain
yield self
end
end
def chain_maybe(klass, condition, *args)
args[1..-1].reduce(klass.send(args.first)) { |r,m| (condition && r.send(m)) || r }
end
or:
def chain_maybe(klass, condition, *args)
first, *others = args
others = [] unless condition
others.reduce(klass.send(first)) { |r,m| r.send(m) }
end
For:
class Klass
def foo
puts 'foo'
self
end
def bar
puts 'bar'
self
end
def baz
puts 'baz'
self
end
end
c = Klass.new
chain_maybe(c, true, :foo, :bar, :baz)
foo
bar
baz
#=> #<Klass:0x007fccea8da388>
chain_maybe(c, false, :foo, :bar, :baz)
foo
#=> #<Klass:0x007fccea8da388>
chain_maybe(c, true, :foo, :bar)
foo
bar
#=> #<Klass:0x007fccea8da388>
chain_maybe(c, true, :bar, :baz)
bar
baz
#=> #<Klass:0x007fccea8da388>
If there is to be a condition for each argument, this can be generalized to:
def chain_maybe(klass, conditions, args)
return nil if conditions.empty?
first, *others = args.zip(conditions).select(&:last).map(&:first)
others.reduce(klass.send(first)) { |r,m| r.send(m) }
end
args = [:foo, :bar, :baz]
chain_maybe(c, [true, true, true], args)
foo
bar
baz
#=> #<Klass:0x007fccea8da388>
chain_maybe(c, [false, true, true], args)
bar
baz
#=> #<Klass:0x007fccea8da388>
chain_maybe(c, [true, false, true], args)
foo
baz
#=> #<Klass:0x007fccea8da388>
You can use tap if the block you are chaining is mutable, i.e: It will change the value of self (as explained here)
However, if you are chaining immutable blocks like QueryMethods, you can use try like:
data
.try { |d| group ? d.group(group) : d }
.try { |d| group ? d.order(order => :desc) : d }
try with nothing else but a block is essentially:
def try
yield self
end
(reference: #try)
class Array
attr_accessor :arr
def initialize(arr)
#arr = arr
end
def self.second(list)
if list[1] != nil
puts list[1]
else
puts nil
end
end
end
([3, 4, 5]).second
You've second as a class method instead of instance method.
Do this instead.
class Array
def second
if self[1].nil?
puts "Nil value"
else
puts self[1]
end
end
end
And by the way you don't need an attr_accessor. That's a code smell.
The keyword self in the definition of a method makes the method a class and not an instance method.
At the moment your method can be called like this:
Array.second([3, 4, 5])
If you want to call is like this:
[3, 4, 5].second
then you need to change your code to something like this:
class Array
def second
if self[1] != nil
puts self[1]
else
puts nil
end
end
end
Furthermore there is no need to check for nil if you print the nil anyway. This allows us to write the same as:
class Array
def second
puts(self[1])
end
end
[3,4,5].second
#=> 4
[].second
#=>
If you want to explicitly print the string "nil" instead of just nil, change to code to:
puts(self[1] | 'nil')
Or:
puts(self.fetch(1) { 'nil' })
I have a method that accepts a method as an argument:
def acceptor_method(received_method)
an_arry.each do |attr|
received_method if some_condition
end
end
This works well if all the received_method does is run through some code:
def some_method
do_something
end
Which is the equivalent of:
def acceptor_method(received_method)
an_arry.each do |attr|
do_something if some_condition
end
end
But what if I want the received_method to break the loop and return a value, as with:
def acceptor_method(received_method)
an_arry.each do |attr|
return true if some_condition
end
end
Unfortunately, this doesn't work:
def some_method
return true
end
As it only returns true for some method, not for acceptor_method--which continues to play through the loop.
So is there a way to send a method that when run is the equivalent of return true?
def acceptor_method
[1, 2, 3, 4].each do |attr|
ret = yield attr
puts attr
end
end
test = acceptor_method do |attr|
break 'test' if attr == 3
end
puts test
outputs:
1
2
test
You can do this using blocks rather than methods. See How can I return something early from a block?
Basically if you have a block with break value and yield to it, the function will return value. Unfortunately I don't see a way to do this using methods, since Ruby really doesn't like having break outside of a block or loop.