why is overloading invalid in ruby.

T

Ted Flethuseo

I don't understand why when I try to overload I get an error. Can I
overload somehow?

#!/usr/bin/env ruby

class Summer
def sum(x)
return x + 2
end

def sum(x,y)
return x + y
end

def sum(x,y,z)
return x + y + z
end
end

s = Summer.new
puts s.sum(3)

ERROR:
ArgumentError: wrong number of arguments (1 for 3)

method sum in summer.rb at line 18
at top level in summer.rb at line 18


Ted
 
A

Andrew Wagner

[Note: parts of this message were removed to make it a legal post.]

I don't understand why when I try to overload I get an error. Can I
overload somehow?

#!/usr/bin/env ruby

class Summer
def sum(x)
return x + 2
end

def sum(x,y)
return x + y
end

def sum(x,y,z)
return x + y + z
end
end

s = Summer.new
puts s.sum(3)

ERROR:
ArgumentError: wrong number of arguments (1 for 3)

method sum in summer.rb at line 18
at top level in summer.rb at line 18


Ted
method with that name, it replaces it. So your class is left with only one
method, the one with 3 arguments. The first 2 get destroyed. The more
ruby-esque way to do this is to use optional parameters. Here's one simple
solution:

def sum(x,y=0,z=0)
return x + y + z
end
 
B

Ben Bleything

I don't understand why when I try to overload I get an error. Can I
overload somehow?

Ruby does not support method overloading. Searching the list archives
should turn up plenty of explanations.

Ben
 
S

Stefano Crocco

I don't understand why when I try to overload I get an error. Can I
overload somehow?

#!/usr/bin/env ruby

class Summer
def sum(x)
return x + 2
end

def sum(x,y)
return x + y
end

def sum(x,y,z)
return x + y + z
end
end

s = Summer.new
puts s.sum(3)

ERROR:
ArgumentError: wrong number of arguments (1 for 3)

method sum in summer.rb at line 18
at top level in summer.rb at line 18


Ted

Ruby doesn't support method overloading. What your code does is giving three
definition of the sum method, with each definition overwriting the previous
one. This means that the first two definitions have no effect, since they're
overwritten by the third. At the end of your code, the sum method is defined
to take three arguments, so you obviously get an error when you try to call it
with only one argument.

You can achieve the same result giving some of the arguments default values:

class Summer

def sum x, y = 2, z = 0
x + y + z
end

end

Now the sum method can be called with one, with two or with three arguments.
In the first case, y will be 2 and z will be three, so the result will be
computed as

x + 2 + 0

which is what you wanted. If it's called with two arguments, x and y will be
given the values passed as argument and z will be 0.

Another approach is to give sum a variable number of arguments, like this:

def sum x, *args
args << 2 if args.empty?
res = x
args.each{|i| res += i}
res
end

The * in front of args in the method definition tells ruby that args is
special: it stands in place of any number of arguments, from 0 upwards. When
the method is called, the first argument is assigned to x. Any other argument
(if any) is put in an array which is assigned to the variable args. In
particular, if sum is called with only one argument, args will be empty, so we
insert the value 2 in it (by the way, you could use Array#inject here, which
is much more elegant than what I wrote).

I hope this helps

Stefano
 
M

Mike Stephens

I guess the answer to your question is "Because you don't need to"

Overloading refers to two quite distinct situations. One is where you
want to involve parameters of different classes. You might want to add
two numbers or a number and a string. Ruby doesn't have anything like
the problem with that compared to more typed languages. The Ruby term is
"Duck Typing" which means Ruby will let you supply miscellaneous objects
and then see if they work in the context.

The second meaning - the one you demonstrated - is different numbers of
parameters. As other have pointed out, you can supply different numbers
of parameters to the same methods as long as you tell Ruby this might
happen and tell it how to deal with all the possibilties - with certain
constraints.

The problem is your example is pretty meaningless. You would be unlikely
to want to write a different method for each sum length.

Think of a more realistic scenario and then see if Ruby doesn't deal
with it.

(In the meantime, notice you really don't need to wrap your code in the
class Summer. I don't think it adds anything.)

(Oh dear, I shouldn't have said that...)
 
T

Ted Flethuseo

Ah that's too bad... I knew I could put some default values for x and y
and z.. getting the same effect sort of...

but this isn't a solution if the methods that I want to "overload" (I
call it
overload, not sure what the proper term should be) are
completely different, and are not differentiated simply by different
values.

Thank you for your reply though.

Ted
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

I don't understand why when I try to overload I get an error. Can I
overload somehow?

#!/usr/bin/env ruby

class Summer
def sum(x)
return x + 2
end

def sum(x,y)
return x + y
end

def sum(x,y,z)
return x + y + z
end
end

s = Summer.new
puts s.sum(3)

ERROR:
ArgumentError: wrong number of arguments (1 for 3)

method sum in summer.rb at line 18
at top level in summer.rb at line 18


Ted

Ruby methods don't really have signatures, so overloading would be quite
difficult.

Disregarding your first sum, which adds 2, for some unknown reason, I would
write the method like this:

def sum(*numbers)
sum = 0
numbers.each { |number| sum += number }
sum
end

Okay, I would probably actually write it like this, which is the same thing,
but less accessible to people who haven't encountered inject or reduce
before.

def sum(*numbers)
numbers.inject { |sum,number| sum + number }
end

It could then be called with any number of arguments and types

sum 1 # => 1
sum 1 , 2 # => 3
sum 5 , 9 , 10 # => 24
sum 8 , 8 , 16 , 32 # => 64
sum 1.5 , 2.3 # => 3.8

In fact, it is flexible enough to take non-numeric types, though you
probably wouldn't ever use such a thing.

sum 'a' , 'b' # => "ab"
sum [1,2] , [3,4] # => [1, 2, 3, 4]
 
J

Jesús Gabriel y Galán

Ah that's too bad... I knew I could put some default values for x and y
and z.. getting the same effect sort of...

but this isn't a solution if the methods that I want to "overload" (I
call it
overload, not sure what the proper term should be) are
completely different, and are not differentiated simply by different
values.

If they are so completely different, maybe the name of the method
should be different.
Can you share a bit more so we can give some recommendations?

Jesus.
 
M

Mike Stephens

Just to clarify what I was proposing (I am prepared to be corrected by
the experts here) - when you specify parameters to a Ruby method, Ruby
passes in variables. In Ruby, variables are references ie pointers to an
object. They of themselves are all the same data type. You can pass in
references to any object type. Ruby doesn't check the type of object
being referred to. One reason is at no point have you indicated what
type Ruby should expect.

At run time, inside the method body, Ruby will attempt to pass the
method name to the object referred to by your parameter. Only at that
point do we need things to match up.

The next step in the story is that = is a special case. It says make the
variable on the left hand side point to the object on the right hand
side. All other similar looking operators are actually methods on an
object. Ruby simply sends the message to the object concerned and sees
what happens.

In this scenario you can see how Ruby can deal with varying types (the
overloading gets shifted to the object being called - n possible object
types gives you n possible overloads.

However there's no corresponding way around the number (and order) of
parameters without the less elegant solutions discussed above.

Nobody sems to complain, so I suggest you don't worry about it. It's
unlikely to cramp your style.
 
D

David Masover

Ah that's too bad... I knew I could put some default values for x and y
and z.. getting the same effect sort of...
=20
but this isn't a solution if the methods that I want to "overload" (I
call it
overload, not sure what the proper term should be) are
completely different, and are not differentiated simply by different
values.

If they're not differentiated by different values, how were you expecting=20
overloading to work? If the problem is that you need the behavior to be=20
different, I think default values still work -- for example:

def sum_or_inverse a, b=3Dnil
if b.nil?
-a
else
a+b
end
end

But I don't really know what you were asking.
I agree with Jes=C3=BAs. We need more details.
 
T

Ted Flethuseo

David Masover wrote in post #978005:
If they're not differentiated by different values, how were you
expecting
overloading to work? If the problem is that you need the behavior to be=
different, I think default values still work -- for example:

def sum_or_inverse a, b=3Dnil
if b.nil?
-a
else
a+b
end
end

But I don't really know what you were asking.
I agree with Jes=C3=BAs. We need more details.

Ok.. I didn't want to post so much code but you asked for it. I am =

trying to read a file, with a number of different methods. The methods =

allow you to interpret the file as an input and output matrices, input =

only or both but with only a number of rows.

class CSVFile
attr_accessor :inFile
attr_accessor :file_data
attr_accessor :in_data
attr_accessor :eek:ut_data
attr_reader :count

def initialize(file)
@inFile =3D file
@file_data =3D []
@Count =3D 0
@in_data =3D []
@out_data =3D []
end

def read_data
if inFile.nil? =3D=3D false
@Count =3D 0
File.foreach(inFile) do |line|
arr =3D line.chomp.split(',')
float_array =3D arr.map { |x| x.to_f }
file_data.push(float_array)
@Count =3D @Count + 1
end
end
return file_data
end

def read_data(num_in, num_out)
if inFile.nil? =3D=3D false
@Count =3D 0
File.foreach(inFile) do |line|
arr =3D line.chomp.split(',')
float_array =3D arr.map { |x| x.to_f }
arr_in =3D []
arr_out =3D []
for i in 0...num_in
arr_in << float_array
end

in_data.push(arr_in)
for j in num_in...(num_in+num_out)
arr_out << float_array[j]
end
out_data.push(arr_out)
@Count =3D @Count + 1
end
end
return file_data
end

def read_data(num_in, num_out, num_lines)
if inFile.nil? =3D=3D false
@Count =3D 0
File.foreach(inFile) do |line|
if @Count >=3D num_lines
break;
end

arr =3D line.chomp.split(',')
float_array =3D arr.map { |x| x.to_f }
arr_in =3D []
arr_out =3D []
for i in 0...num_in
arr_in << float_array
end

in_data.push(arr_in)
for j in num_in...(num_in+num_out)
arr_out << float_array[j]
end
out_data.push(arr_out)
@Count =3D @Count + 1
end
end
return file_data
end
end

-- =

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

botp

I don't understand why when I try to overload I get an error. Can I
overload somehow?

Hi Ted,

sorry no overloading in ruby. overloading gives fixed/absolute power
to compiler/interpreter. ruby is dynamic, everything can be changed at
runtime, anytime. second, ruby is oo, ojbect first progg,
polymorph-based ie.

=A0def sum(x)
=A0 =A0return x + 2
^^
lose the return, ruby does not need it here

as others have mentioned, you can use starred args and default params
to mimic overloading. if that does not fit your taste, you can use
named params or hash (it fits mine :)

the advantage of named params over static signature/position-based
param is that you have more control of your method since it is really
*the* params (wc are objects by themselves) that do the playing, not
the positioning (again, emphasizing o-o-ness). ergo, you have better
control and you can test/debug the methods a lot better.

now if that still does not suit your taste, you can do polymorphism,
wc of course comes natural in o-o langgs.

anyway, the ff code shows how to scheme thru named parameters way,

eg,

class Summer
def sum(opt=3D{:none=3D>"none entered :)"})
case opt.keys.sort
when [:none]
opt[:none]
when [:x]
sumx opt[:x]
when [:x, :y]
sumxy opt[:x], opt[:y]
when [:x, :y, :z]
# you can place more conditions here
sumxyz opt[:x], opt[:y], opt[:z]
when [:a, :x, :y, :z]
sumxyz_xa opt[:x], opt[:y], opt[:z], opt[:a]
else
# you can place other conditions and methods here, do anything
end
end

private

def sumx x
x + 2
end

def sumxy x,y
x + y
end

def sumxyz x,y,z
x + y + z
end

def sumxyz_xa x,y,z,a
sumxyz(x,y,z) ** a
end
end
#=3D> nil

s =3D Summer.new
#=3D> #<Summer:0x918e350>
puts s.sum()
none entered :)
#=3D> nil
puts s.sum(x:3)
5
#=3D> nil
# note also that the params have been disarranged to show flex
puts s.sum(x:3,y:4)
7
#=3D> nil
puts s.sum(z:5,x:3,y:4)
12
#=3D> nil
# this last sample call previous method and raises that to power :a
puts s.sum(y:4,a:6,x:3,z:5)
2985984
#=3D> nil

best regard -botp
 
B

botp

Ok.. I didn't want to post so much code but you asked for it. I am
trying to read a file, with a number of different methods. The methods
allow you to interpret the file as an input and output matrices, input
only or both but with only a number of rows.


ok, you've defined the class, thanks.
pls also show how'd you call/use so we do not have to invent here.

best regards -botp
 
B

brabuhr

David Masover wrote in post #978005:

Ok.. I didn't want to post so much code but you asked for it. I am
trying to read a file, with a number of different methods. The methods
allow you to interpret the file as an input and output matrices, input
only or both but with only a number of rows.

Infinity =3D 1.0/0

def read_data(num_in =3D nil, num_out =3D nil, num_lines =3D Infinity)
=A0=A0unless inFile.nil?
=A0=A0 =A0@count =3D 0

=A0=A0 =A0File.foreach(inFile) do |line|
# def read_data(num_in, num_out, num_lines)
=A0 =A0 =A0break if @Count >=3D num_lines

=A0 =A0arr =3D line.chomp.split(',')
=A0 =A0=A0 float_array =3D arr.map { |x| x.to_f }

# def read_data(num_in, num_out)
if num_in and num_out
=A0 =A0 =A0arr_in =3D []
=A0 =A0=A0 =A0arr_out =3D []
=A0 =A0 =A0for i in 0...num_in
=A0 =A0 =A0 =A0 =A0arr_in << float_array
=A0 =A0 =A0 =A0 end

=A0 =A0 =A0 =A0in_data.push(arr_in)
=A0 =A0 =A0 =A0for j in num_in...(num_in+num_out)
=A0 =A0 =A0 =A0arr_out << float_array[j]
=A0 =A0=A0 =A0end
=A0 =A0 =A0out_data.push(arr_out)
elsif num_in or num_out
raise ArgumentError
else
# def read_data
file_data.push(float_array)
=A0 end

=A0 =A0=A0@count =3D @Count + 1
=A0 =A0 end

=A0 =A0 file_data
end
end
 
J

John W Higgins

Good Evening,

David Masover wrote in post #978005:

Ok.. I didn't want to post so much code but you asked for it. I am
trying to read a file, with a number of different methods. The methods
allow you to interpret the file as an input and output matrices, input
only or both but with only a number of rows.
How about this - I'm unsure of some of the variables - they seem a little
strange but I'm sure you can debug any issues in that respect - I just used
pretty much what you provided.

def read_data(num_in =3D nil, num_out =3D nil, num_lines =3D nil)
#This just looks a little nicer than the if .nil? =3D=3D false
unless @inFile.nil?
#Either we are provided the number of lines to use or we use them all
num_lines ||=3D @inFile.length
#Instead of counting where we are we just limit our group to what we
want
myFiles =3D @inFile[0...num_lines]
#Use our limited dataset
File.foreach(myFiles) do |line|
arr =3D line.chomp.split(',')
float_array =3D arr.map { |x| x.to_f }
#Make a choice based on the num_in variable
if num_in.nil?
@file_data.push(float_array)
else
#Much simpler here - no need for the loops and temp vars
#Just grab what we need and move along
in_data << float_array[0...num_in]
out_data << float_array[num_in...(num_in+num_out)]
end
end
end

return @file_data
end
 
D

David Masover

David Masover wrote in post #978005:
=20
If they're not differentiated by different values, how were you
expecting
overloading to work? If the problem is that you need the behavior to be
different, I think default values still work -- for example:
=20
def sum_or_inverse a, b=3Dnil
=20
if b.nil?
=20
-a
=20
else
=20
a+b
=20
end
=20
end
=20
But I don't really know what you were asking.
I agree with Jes=C3=BAs. We need more details.
=20
Ok.. I didn't want to post so much code but you asked for it. I am
trying to read a file, with a number of different methods. The methods
allow you to interpret the file as an input and output matrices, input
only or both but with only a number of rows.
=20
class CSVFile
attr_accessor :inFile
attr_accessor :file_data
attr_accessor :in_data
attr_accessor :eek:ut_data
attr_reader :count
=20
def initialize(file)
@inFile =3D file
@file_data =3D []
@Count =3D 0
@in_data =3D []
@out_data =3D []
end
=20
def read_data
if inFile.nil? =3D=3D false
@Count =3D 0
File.foreach(inFile) do |line|
arr =3D line.chomp.split(',')
float_array =3D arr.map { |x| x.to_f }
file_data.push(float_array)
@Count =3D @Count + 1
end
end
return file_data
end
=20
def read_data(num_in, num_out)
if inFile.nil? =3D=3D false
@Count =3D 0
File.foreach(inFile) do |line|
arr =3D line.chomp.split(',')
float_array =3D arr.map { |x| x.to_f }
arr_in =3D []
arr_out =3D []
for i in 0...num_in
arr_in << float_array
end
=20
in_data.push(arr_in)
for j in num_in...(num_in+num_out)
arr_out << float_array[j]
end
out_data.push(arr_out)
@Count =3D @Count + 1
end
end
return file_data
end
=20
def read_data(num_in, num_out, num_lines)
if inFile.nil? =3D=3D false
@Count =3D 0
File.foreach(inFile) do |line|
if @Count >=3D num_lines
break;
end
=20
arr =3D line.chomp.split(',')
float_array =3D arr.map { |x| x.to_f }
arr_in =3D []
arr_out =3D []
for i in 0...num_in
arr_in << float_array
end
=20
in_data.push(arr_in)
for j in num_in...(num_in+num_out)
arr_out << float_array[j]
end
out_data.push(arr_out)
@Count =3D @Count + 1
end
end
return file_data
end
end


Yeah, there's a lot of duplicate stuff there. Also, why are you doing this=
=20
when there's a CSV class in the standard library?

http://ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html

Anyway, it looks like you're copying and pasting a _lot_ between those=20
methods, so it also looks like if you DRYed it up a bit, it'd work just fin=
e=20
with default arguments. Also, there's a bunch of ways this could be more=20
idiomatic... Here's a rough guess at what you're trying to do, at least for=
=20
this one method:

def read_data num_in=3Dnil, num_out=3Dnil, num_lines=3Dnil
# This does what you said above. Are you sure that's right?
# Shouldn't it raise an error or something?
return file_data if inFile.nil?

# If I were you, I'd have the 'count' method pass through to
# file_data.length or something, so this could be a local...
@Count =3D 0

# Object-oriented programming means letting the object
# figure out what to do, instead of calling functions
# with that object. The File.foreach was a code smell to me.
inFile.each_line do |line|
break if num_lines && @Count >=3D num_lines

# Note that, unlike a proper CSV parser, this won't handle escaped comm=
as.
arr =3D line.chomp.split(',').map(&:to_f)

if num_in
# An each would be better than a foreach IMO, but if you want
# the best speed and readability, why not rely on stuff arrays
# already know how to do instead of writing the loop yourself?
arr_in.push arr[0...num_in]

if num_out
# num_in and num_out are both counts, right?
arr_out.push arr[num_in,num_out]
end
end

@Count +=3D 1
end

file_data
end


Warning: I haven't tested the above code at all, but it _looks_ like it can=
=20
replace the read_data methods you wrote. Let me know. Still, I mostly did i=
t=20
as an exercise, because if you were planning to actually work with CSVs, I'=
d=20
think your code should start with:

require 'csv'
 
C

Charles Oliver Nutter


FWIW, I'd love for Ruby to support operator overloading. JRuby
supports it in Java-based Ruby methods (like the core classes) by
splitting the arities up into different call paths. I hope to do the
same under the covers for "overloaded" optional args forms, so that
this:

def foo(a, b = 1, c = 2)

would compile to the equivalent of this overloaded form:

def foo(a) foo(a, 1, 2)
def foo(a, b) foo(a, b, 2)
def foo(a, b, c) ...

When you look at optional args that way it doesn't seem so odd to take
the next leap and say ruby should allow separate logic for those
different paths rather than simply chaining them. For example, it's
uncommon but sometimes you'll see this form:

def foo(a, b = (c = true; 1)) ...

'b' is an optional arg that, when set to its optional value (i.e.
nothing passed in), also sets c = true. So you can check c to see if
the value of b came from the optional value or not. Gross!

If instead you could actually overload, it would simply be two methods:

def foo(a); b = 2; <logic for one-arg call>
def foo(a, b); <logic for two-arg call>

The fact that people have to use tricks like b = (c = true; 2) make me
think there's an opportunity here.

And it would be even cooler if Ruby supported some form of type-driven
pattern matching, so you could have different method bodies for
different input types rather than checking them over and over again.
But that's a post for another day :)

- Charlie
 
A

Adam Prescott

[Note: parts of this message were removed to make it a legal post.]

And it would be even cooler if Ruby supported some form of type-driven
pattern matching, so you could have different method bodies for
different input types rather than checking them over and over again.
But that's a post for another day :)

Not very quackish.
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

Not very quackish.

Well, you could reinterpret "type driven" to be "respond_to driven". Almost
the same sentiment, but more embracing of OO as described in Rick's talk (
http://confreaks.net/videos/461-rubyconf2010-objects-are-just-objects-aren-t-they
).

Personally, I found it weird to not have overloading when I came from Java,
because I had fallen into a series of patterns based around it that I really
liked (one canonical method, with several overload ones for convenience,
that just take their arguments and translate them to the appropriate method
call of the real one). But now, I think Ruby's bag of argument tricks are
much more powerful. Initialization blocks, optional params through hash
support, optional arguments, etc I think are very powerful (might be nice if
the language supported the hashes a bit better), and really, I almost never
miss overloading, and I think the things you would lose by supporting it
would not be worth it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top