How might I treat nil values as zero when adding numbers? - ruby

I have a method that looks like this:
def calculate_the_thing(hsh)
hsh[:x] + hsh[:y] + hsh[:z]
end
which takes something like this:
{:x => 5, :y => nil, :z => 2, :a => 5}
I'd like to patch some classes so that when the + method gets a nil value, it treats it as zero. Seems reasonable. How might I do that?

As #jforberg points out, you can just use the #to_i method which will return 0 for nil.
def calculate_the_thing(hsh)
hsh[:x].to_i + hsh[:y].to_i + hsh[:z].to_i
end
Alternatively, you can also define the hash with an automatic default value...
hsh = Hash.new{0}
But if you have a method that explicitly puts nil as a hash value that will override the default value.

You need to monkey-patch Nilclass to coerce nil to an integer. Here is the code:
class NilClass
def coerce(n)
return [0, n] if n.is_a? Numeric
super
end
end
1 + nil
#=> 1
Have a look at this thread - In Ruby, how does coerce() actually work? - to understand the concept of coerce.
However, there is an issue with above code:
nil + 1
#=> undefined method `+' for nil:NilClass (NoMethodError)
To fix this problem, you will have to define + method on NilClass
class NilClass
def +(param)
param + self
end
end
nil + 1
#=> 1
If we try to get adventurous and try:
nil * 10
#=> undefined method `*' for nil:NilClass (NoMethodError)
By being adventurous, lets handle all such undefined methods by implementing our own method_missing handler.
class NilClass
def method_missing m, *args, &block
args.first.send(m, self, &block) if args.size == 1
end
end
p nil * 1
#=> 0
Next, lets try:
nil + "hello"
# '+': can't convert NilClass to String (NilClass#to_str gives NilClass) (TypeError)
Lets fix this as well
class NilClass
def to_str
""
end
end
nil + "hello"
#=> "hello"
Next, lets try this:
nil + [1,2,3]
#=> '+': can't convert NilClass to Array (NilClass#to_ary gives NilClass) (TypeError)
Lets fix it:
class NilClass
def to_ary
[]
end
end
nil + [1,2,3]
#=> [1, 2, 3]
We now have this version of NilClass:
class NilClass
def coerce(n)
return [0, n] if n.is_a? Numeric
super
end
def method_missing m, *args, &block
args.first.send(m, self, &block) if args.size == 1
end
def to_str
""
end
def to_ary
[]
end
end
Caution:: Above code shows that what you want to do can be done. However, this should be used only for experimental and learning purpose. It is really not feasible to make nil behave like other operand in all operations and you will end up monkey-patching NilClass to no end.
Hence, its better to stay off from this kind of monkey patching to avoid scary surprises to future Rubyists who will be maintaining your code.

Related

Why is the programme returning: `<main>': undefined method `second' for main:Object (NoMethodError)?

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' })

what happened when pass a method to iterator method

As we know, wo can pass a method to a iterator method by a &: prefix.
For example:
["a", "b"].map(&:upcase) #=> ["A", "B"]
def rettwo
2
end
["a", "b"].map(&:rettwo) #=> [2, 2]
Here is the problem, when I write a method, pass a method with &: prefix to it, I got a error message: "ArgumentError: no receiver given".
Let me show the code:
def a_simple_method &proc
puts proc.class # it shows `Proc`
proc.call
end
def a_iterator_method
puts yield
end
a_simple_method &:rettwo #=> ArgumentError: no receiver given
a_iterator_method &:rettwo #=> ArgumentError: no receiver given
What do I missing, How the map like method of Array handle it
Here's what works. Explanation below.
class String
def rettwo
self + self
end
end
def a_simple_method &proc
proc.call('a')
end
def a_iterator_method
yield 'b'
end
a_simple_method(&:rettwo) # => "aa"
a_iterator_method(&:rettwo) # => "bb"
The &: construct is called Symbol#to_proc. It turns symbol into a proc. This proc expects a receiver as a first argument. The remaining arguments are used to call the proc. You're not passing any arguments, hence the "receiver not given" error.
Here's a demonstration of additional arguments:
class String
def say name
"#{self} #{name}"
end
end
def a_simple_method &proc
proc.call('hello', 'ruby')
end
a_simple_method(&:say) # => "hello ruby"
Here's a definition of Symbol#to_proc from some blog post from 2008. Modern Symbol#to_proc seems to be implemented in C, but this can still help the understanding.
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end

Some doubts w.r.t. short-circuit (||=) operator

I noticed some interesting use of ||= as below -
Code 1
array_1 ||= begin
(1..5).to_a.map {|el| el*10}
end
puts array_1.to_s # [10,20,30,40,50]
So I executed its following modified version -
Code 2
array_2 ||= def some_method
(1..5).to_a.map {|el| el*10}
end
puts array_2 # prints nothing
puts array_2.class # NilClass
puts some_method.to_s # [10,20,30,40,50]
Why does array_2 gets initialised to nil?
Is it possible to invoke some_method using array_2 object? how?
UPDATE
This is what I did for question 2
array_2 ||= "We've got #{def some_method;(1..5).to_a.map {|el| el*10};end;array_2.send(:some_method)}"
puts array_2 # We've got [10, 20, 30, 40, 50]
The return value of a method definition expression (def) is implementation-defined. Most Ruby implementations simply return nil from a def expression, but Rubinius, for example, returns the compiled code of the method wrapped up in a CompiledMethod object.
When you declare any method it will simply return nil but when you call any method it will return the o/p of the last line return in that method unless you used return keyword.
Following should work for you
def some_method
return (1..5).to_a.map {|el| el*10}
end
array_2 ||=some_method

Mute output of IRB for a specific class

Im working with a class, containing large instance arrays.
Whenever I initialize a class like this, e.g. i = Image.new, I get a lot of junk output from my arrays (#r, #g, #b - ~300k values each).
class Image
def initialize(width=640, height=480, brightness=64)
#width, #height, #brightness = width, height, brightness
self.load('usart.dat')
end
def load(file='usart.dat')
self.reset
f = IO.read(file, #height * #width * 2, 0)
# Parsing the datafile, saving data in #r, #g, #b, #gray etc
end
return self
end
# ... More methods
end
Question is, how can I either silence the output (all results are saved to a file, never viewed in the console) or make an initializer NOT inspect self. I want to return self, since I want to stack methods, e.g. image.load('file').binary.grayscale.save(:bin).
A common workaround for this type of problem is to just add ; nil in IRB, e.g. i = Image.new ; nil. This way the last evaluated expression is nil which will also be returned.
>> Foo.new
=> #<Foo:0x00000000e3b9d0 #a=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]>
>> Foo.new ; nil
=> nil
The output you are seeing is the result of your object's to_s (or inspect). You can define/overwrite to_s (or inspect) for your classes to produce less output. E.g.
irb(main):001:0> class Foo
irb(main):002:1> def initialize
irb(main):003:2> #a = #b = #c = #d = 42
irb(main):004:2> end
irb(main):005:1>
irb(main):006:1* def to_s
irb(main):007:2> 'a Foo object'
irb(main):008:2> end
irb(main):009:1> end
=> nil
irb(main):010:0> h = Foo.new
=> a Foo object
irb(main):011:0>
Regarding to_s/inspect: irb calls inspect which normally just calls to_s (see ruby-doc). So, defining to_s should normally work. However, if there is an inspect not calling to_s in the class (or its ancestors), this inspect has to be overwritten.
Disable irb echoing:
conf.echo = false
The gem utility_belt will give you commands quiet and verbose to disable/enable the output (and more).
If you changed the return value of Foo.new to not return the new Foo object, you would never be able to assign the new Foo to any variable. If the only concern is occasionally scrolling the console view, use the ";nil" approach.

What happend here? (nil in Ruby)

p parent.class #=> NilClass # ok.
p !!parent # => false # as expected.
p parent.object_id # => 17006820 # should be 4
p parent && parent.foo # => NoMethodError foo # should be nil-guarded
Where does this object come from?
Possibly something like this:
class BlankSlate
instance_methods.each do |m|
# Undefine all but a few methods. Various implementations leave different
# methods behind.
undef_method(m) unless m.to_s == "object_id"
end
end
class Foo < BlankSlate
def method_missing(*args)
delegate.send(*args)
end
def delegate
# This probably contains an error and returns nil accidentally.
nil
end
end
parent = Foo.new
p parent.class
#=> NilClass
p !!parent
#=> false
p parent.object_id
#=> 2157246780
p parent && parent.foo
#=> NoMethodError: undefined method `foo' for nil:NilClass
Creating BlankSlate or BasicObject is a common pattern (before it was added to core Ruby as of version 1.9). It serves to create objects that will do something special with any method they are sent, or heavily delegate their behaviour to a different class. The downside is that it may introduce strange behaviour like this.

Resources