Errors with respond_to? and inject

Hello,

This is my first post, and i hope i use the correct topic.
There are two errors i do not understand in the code below

I’ve commented out the line that don’t work, and the line directly below
works just fine
Finally i put the errors when using the problematic lines

I do not see any errors in the commented line, if someone do i’ll be
happy to understand what is wrong

thx in advance

Here is the code

class Item
attr_reader :name, :price

def initialize name, price
@name, @price = name, price
end

def + item

price + item.price if item.respond_to? price #### 1

price + item.price if item.is_a? Item

end
end

class Items
def initialize
@items = []
end

def << item
@items << item if item.is_a? Item
end

def price
@items.collect { |i| i.price }.inject(:+)

@items.inject(:+) #### 2

end
end

i = Items.new

i << Item.new(‘a’, 1)
i << Item.new(‘b’, 2)
i << Item.new(‘c’, 3)

puts i.price

The errors

1

./test2.rb:11:in `respond_to?’: 1 is not a symbol (TypeError)

2

./test2.rb:25:in `+’: Item can’t be coerced into Fixnum (TypeError)

Thx for ur response Sam
For the first issue indeed (newbie to ruby :wink:

For the second happy there is a fix.
But i don’t understand.
For what i’ve read inject combines all element of an array by applying a
method or block on each
How come inject uses the Numeric#+ instead of my Item#+ since all
elements are items???
That puzzled me

Does someone understands that? It don’t make sens to me

On 01/12/11 09:03, Sébastien M. wrote:

happy to understand what is wrong
end
end

1

./test2.rb:11:in `respond_to?’: 1 is not a symbol (TypeError)

2

./test2.rb:25:in `+’: Item can’t be coerced into Fixnum (TypeError)

The first error is pretty straight forward to sort out. The #respond_to?
method is expecting a symbol, so changing ‘price’ to ‘:price’ fixes
that. The other error is due (I believe) to the fact that the inject
ends up trying to use Numeric#+ instead of your Item#+ method when the
object on the left of the operator is a number. To fix this you can
define Item#coerce.

This is likely not the best way to do it, but it appears to work;

class Item
attr_reader :name, :price

def initialize name, price
@name, @price = name, price
end

def coerce arg
if arg.is_a? Numeric
[arg, price]
end
end

def + item
price + item.price if item.respond_to? :price
end
end

class Items
def initialize
@items = []
end

def << item
@items << item if item.is_a? Item
end

def price
@items.inject(:+)
end
end

i = Items.new

i << Item.new(‘a’, 1)
i << Item.new(‘b’, 2)
i << Item.new(‘c’, 3)

puts i.price

Hope that helps
Sam

Well it makes sens

Just a bit confused by this coerce method i just discovered
anyway thanks for ur fast answer
and maybe someone who knows how inject is implemented might confirm ur
assertion

Seb

On 01/12/11 11:07, Sébastien M. wrote:

Does someone understands that? It don’t make sens to me

I won’t pretend to know what is happening under the hood, but my simple
minded assumption is that inside the inject, the method is being called
iteratively with a temp var holding the result.

If you were to do item1+item2, the result would be a Numeric object. If
you then did result+item3, you are suddenly calling Numeric#+ rather
than Item#+ (because result is the receiver)

I’m sure someone else will be able to clarify this for you and point out
if my assumption is horribly wrong/ misguided.

Sam

Adding some print statements in the inject helps trace what is
happening. Starting with Sam’s code:

class Items

def price
@items.inject do |a,b|
puts “a = #{a} of class #{a.class}, b = #{b} of class #{b.class}”
a+b
end
end
end

I also added a to_s method on Item:

class Item

def to_s
“Item #{@name} costs #{@price}”
end
end

The output is:

a = Item a costs 1 of class Item, b = Item b costs 2 of class Item
a = 3 of class Fixnum, b = Item c costs 3 of class Item
6

The first argument to the inject block (‘a’) is the accumulated value.
Initially, this value is set as the first item in the array, so in the
first line we add two ‘Item’ instances. The return value of the first
addition is a number, so that partial sum is used in the second line:
now we are adding a ‘Fixnum’ to an ‘Item’. Sam’s coerce solution makes
this addition work.

A different approach is to make the function used in inject add up and
return
objects of the same type. So the + method on the Item class
would return an instance of Item:

class Item

def + item
Item.new(“acc”, @price + item.price)
end
end

The output now shows only Item instances are being passed around:

a = Item a costs 1 of class Item, b = Item b costs 2 of class Item
a = Item acc costs 3 of class Item, b = Item c costs 3 of class Item
Item acc costs 6

This way, you don’t need the ‘coerce’ or ‘respond_to?’ methods. You
might want to alter the Items#price method to return the final sum only:

class Items

def price
@items.inject(:+).price # return the ‘price’ of result
end
end

Quick question though

Why don(t u need the ‘respond_to?’ method
We still have to check if object has a price method in order to return
nil
if not. Or is there something i missed?

Nicely done

Discovering ruby and falling in…

Thanks for the reactivity as well

Sébastien M. wrote in post #1034503:

Quick question though

Why don(t u need the ‘respond_to?’ method
We still have to check if object has a price method in order to return
nil
if not. Or is there something i missed?

You need respond_to? if you may pass in a non-Item instance to be added
to an Item (or rather, as this is Ruby, an instance which does not
respond to the ‘price’ method). Myself, I would try not to do that, but
that depends on the design of the rest of your program.

-----Messaggio originale-----
Da: Peter Lane [mailto:[email protected]]
Inviato: gioved 1 dicembre 2011 13:04
A: ruby-talk ML
Oggetto: Re: errors with respond_to? and inject

Sbastien M. wrote in post #1034503:

Quick question though

Why don(t u need the ‘respond_to?’ method We still have to check if
object has a price method in order to return nil if not. Or is there
something i missed?

You need respond_to? if you may pass in a non-Item instance to be added
to
an Item (or rather, as this is Ruby, an instance which does not respond
to
the ‘price’ method). Myself, I would try not to do that, but that
depends
on the design of the rest of your program.


Posted via http://www.ruby-forum.com/.


Caselle da 1GB, trasmetti allegati fino a 3GB e in piu’ IMAP, POP3 e
SMTP autenticato? GRATIS solo con Email.it http://www.email.it/f

Sponsor:
Conto Arancio al 4,20%. Soldi sempre disponibili, zero spese, aprilo in
due minuti!
Clicca qui: http://adv.email.it/cgi-bin/foclick.cgi?mid920&d)-12