`initialize': wrong number of arguments (given 3, expected 0) (ArgumentError) - ruby

I have the following bit of code:
load 'Point.rb'
class Triangle
def initialize(x, y , z)
if !x.is_a?(Point) || !y.is_a?(Point) || !z.is_a?(Point)
puts "Invalid data, a Triangle can only be initialized through points"
else
#x=x
#y=y
#z=z
#type=type(x, y, z)
end
end
def type(x, y, z)
if x.distance(y) == y.distance(z) && y.distance(z) == z.distance(x)
return "equilateral"
elsif x.distance(y)== y.distance(z) || y.distance(z) == z.distance(x) || z.distance(x) == x.distance(y)
return "isosceles"
else
return "scalene"
end
end
attr_accessor :type
end
I'm calling the method like this:
load 'Triangle.rb'
x=Point.new(0,0)
y=Point.new(1,1)
z=Point.new(2,0)
triangle=Triangle.new x, y, z
puts triangle.type
The class Point is as follows:
class Point
def initialize (x=0, y=0)
#x=x.to_i
#y=y.to_i
end
def ==(point)
if #x==point.x && #y==point.y
return true
else
return false
end
end
def distance(point)
Math.hypot((#x-point.x),(#y-point.y))
end
attr_accessor :y
attr_accessor :x
end
The error is as follows:
Triangle.rb:11:in `initialize': wrong number of arguments (given 3, expected 0) (ArgumentError)
from Triangle_test.rb:6:in `new'
from Triangle_test.rb:6:in `<main>'
Please tell if the whole code is just garbage.

In your Triangle class you have method type which accepts three parameters, and then below you have attr_accessor :type which overwrites that 3-parameters method with a parameterless getter.
That's why you get that error when you do this in the initializer
#type=type(x, y, z)

Here's a cleaned-up version of your code:
removed unneeded if's
removed unneeded return's
defined a private calculate_type method
replaced attr_accessor with attr_reader
renamed x,y,z with a,b,c to avoid confusion between coordinates and points
class Point
attr_reader :x, :y
def initialize(x = 0, y = 0)
#x = x.to_i
#y = y.to_i
end
def ==(point)
#x == point.x && #y == point.y
end
def distance(point)
Math.hypot((#x - point.x), (#y - point.y))
end
end
class Triangle
attr_reader :a, :b, :c, :type
def initialize(a, b, c)
raise 'Invalid data, a Triangle can only be initialized through points' unless [a, b, c].all? { |p| p.is_a?(Point) }
#a, #b, #c = a, b, c
#type = calculate_type
end
private
def calculate_type
if a.distance(b) == b.distance(c) && b.distance(c) == c.distance(a)
'equilateral'
elsif a.distance(b) == b.distance(c) || b.distance(c) == c.distance(a) || c.distance(a) == a.distance(b)
'isosceles'
else
'scalene'
end
end
end
a = Point.new(0, 0)
b = Point.new(1, 1)
c = Point.new(2, 0)
triangle = Triangle.new a, b, c
puts triangle.type
# isosceles

Related

Does overriding == for a object change the behavior of include? method of an array?

Example:
class CustomObject
.....
def ==(other)
self.x == other.x && self.y =! other.y
end
.....
end
array_of_custom_objects = CustomObject.load_for(company_id: company_id)
new_custom_object = CustomObject.new(....)
array_of_custom_objects.include? new_custom_object
My question is does the array include? method compare two objects bases on the defination of == method?
Bascially, will the above code determine whether my new_custom_object is included in the array of CustomObject by evaluating the overridden == method for each insance of CustomObject in the array with new_custom_object?
My question is does the array include? method compare two objects bases on the defination of == method?
Yes. As said in: https://ruby-doc.org/3.2.0/Array.html#method-i-include-3F
include?(obj) → true or false click to toggle source
Returns true if for some index i in self, obj == self[i]; otherwise false:
Seems to be working, (though I'm not sure if this is the most optimal way of doing things as we don't know the context of your code):
class CustomObject
attr_reader :x, :y, :z
def initialize(x, y, z)
#x = x
#y = y
#z = z
end
def ==(other)
self.x == other.x && self.y != other.y
end
end
custom_objects = []
new_custom_object_1 = CustomObject.new(1, 2, 3)
custom_objects << new_custom_object_1
new_custom_object_2 = CustomObject.new(2, 3, 4)
custom_objects << new_custom_object_2
search_object = CustomObject.new(2, 7, 4) # 2 == 2 && 3 != 7
puts custom_objects.include?(search_object)
# => true
search_object = CustomObject.new(2, 3, 4) # 2 == 2 && 3 != 3
puts custom_objects.include?(search_object)
# => false

Is there a Ruby method for determining if all instance variables of two instances of the same class are equal?

Is there a Ruby method for comparing two objects based on whether all of their instance variables are equal? The method would behave like this code.
class Coordinates
attr_reader :x, :y
def initialize(x, y)
#x = x
#y = y
end
end
coordinates1 = Coordinates.new(0, 0)
coordinates2 = Coordinates.new(0, 0)
coordinates3 = Coordinates.new(1, 0)
compare(coordinates1, coordinates1) # => true
compare(coordinates1, coordinates2) # => true
compare(coordinates1, coordinates3) # => false
Does this method or something similar exist?
There is no built-in method for this, but you could quite easily write one. However, I think you're asking an XY question.
Here is what I think the question is supposed to say:
How should I define a method to check that two Coordinates instances are equal?
And here's my answer:
Define a custom == method:
class Coordinates
attr_reader :x, :y
def initialize(x, y)
#x = x
#y = y
end
def ==(other)
return super unless other.is_a?(Coordinates)
x == other.x && y == other.y
end
end
...But in the spirit of StackOverflow, here's some meta-programming to check whether all instance variables have the same name and value:
# returns true if all objects have the same instance variable names and values
def compare(*objects)
objects.map do |object|
object.instance_variables.map do |var_name|
[var_name, object.instance_variable_get(var_name)]
end
end.uniq.count == 1
end
Case 1
class A
def initialize(x,y)
#x = x
#y = y
end
def m
#x = 5
#y = 6
end
end
a1 = A.new(1,2)
#=> #<A:0x00005d22a3878048 #x=1, #y=2>
a1.m
a1 #=> #<A:0x00005d22a3878048 #x=5, #y=6>
a2 = A.new(3,4)
#=> #<A:0x00005d22a38b5330 #x=3, #y=4>
a2.m
a2 #=> #<A:0x00005d22a38b5330 #x=5, #y=6>
Then,
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> true
tells us that the values of #x and the values of #y are the same for both instances.
Case 2
Now let's change the code so that another instance variable is added conditionally.
class A
def initialize(x,y)
#x = x
#y = y
end
def m
#z = 3 if #x == 3
#x = 5
#y = 6
end
end
a1 = A.new(1,2)
#=> #<A:0x000057d1fd563c78 #x=1, #y=2>
a1.m
a1 #=> #<A:0x000057d1fd27f200 #x=5, #y=6>
a2 = A.new(3,4)
#=> #<A:0x000057d1fd57cb38 #x=3, #y=4>
a2.m
a2 #=> #<A:0x000057d1fd2f9e10 #x=5, #y=6, #z=3>
At this point are all instance variables of one of these instances equal to the corresponding instance variable of the other instance? No, because a2 has an additional instance variable, #z. Therefore,
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> true
gives the wrong answer, for obvious reasons. Perhaps we could test as follows:
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) } &&
a2.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> true && false => false
This has a gotcha, however, if #z equals nil.
Case 3
class A
def initialize(x,y)
#x = x
#y = y
end
def m
#z = nil if #x == 3
#x = 5
#y = 6
end
end
a1 = A.new(1,2)
#=> #<A:0x000057d1fd2d18e8 #x=1, #y=2>
a1.m
a1 #=> #<A:0x000057d1fd2d18e8 #x=5, #y=6>
a2 = A.new(3,4)
#=> #<A:0x000057d1fd46b460 #x=3, #y=4>
a2.m
a2
#=> #<A:0x000057d1fd46b460 #x=5, #y=6, #z=nil>
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) } &&
a2.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> true && true => true
We obtain this incorrect result because:
class A
end
A.new.instance_variable_get(:#z)
#=> nil
We therefore must confirm that if one instance has an instance variable named e, so does the other instance, and that each pair of instance variables with the same name are equal. One way to do that is as follows:
(a1.instance_variables.sort == a2.instance_variables.sort) &&
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> false && true => false
See Enumerable#all?, Object#instance_variables and Object#instance_variable_get.

Keeping track of the path, Knights travel

So far my shortest path method will stop when it reaches the goal position, printing out everything it did along the way. I would like to know how I could go about implementing parent positions so I can print the path along with the goal. This is an assignment.
class Knight
attr_accessor :x, :y, :prev_position, :moves
def initialize(position)
#x = position[0]
#y = position[1]
#prev_position = nil
#moves = [
[-1,-2],
[-2,-1],
[-2,+1],
[-1,+2],
[+1,-2],
[+2,-1],
[+2,+1],
[+1,+2]]
end
def possible
move_list = Array.new
#moves.each do |moves|
x = #x + moves[0]
y = #y + moves[1]
if x.between?(0,7)
if y.between?(0,7)
move_list << [x,y]
end
end
end
move_list
end
end
def shortest_path(position,goal)
paths = Array.new
#start_knight = Knight.new(position)
until #start_knight.x == goal[0] and
#start_knight.y == goal[1]
#start_knight.possible.each do |p| paths << p end
shifted = paths.shift
#start_knight.x = shifted[0]
#start_knight.y = shifted[1]
puts "[#{#start_knight.x},#{#start_knight.y}]"
end
end
shortest_path([0,0],[7,7])

Setting method local variables from a proc

If I have a class with two instance variables #x and #y, I can change them from a proc using self.instance_exec:
class Location
attr_accessor :x, :y
def initialize
#x = 0
#y = 0
end
def set &block
self.instance_exec(&block)
end
end
location = Location.new
location.set do
#x = rand(100)
#y = rand(100)
end
puts location.x
puts location.y
If I have a class with a method set with two local variables x and y, I can use proc return values to set them:
class Location
def set &block
x = 0;
y = 0;
x, y = block.call()
# do something with x and y
puts x
puts y
end
end
location = Location.new
location.set do
x = rand(100)
y = rand(100)
[x, y]
end
Is there a way to access the set method local variables x and y from the proc without using return values?
You can do it, sort of, but it isn't pretty
There is a way for block to set a variable in a calling method, but it isn't pretty. You can pass in a binding, then eval some code using the binding:
def foo(binding)
binding.eval "x = 2"
end
x = 1
foo(binding)
p x # => 2
Blocks also carry with them the binding in which they were defined, so if a block is being passed, then:
def foo(&block)
block.binding.eval "x = 2"
end
x = 1
foo {}
p x # => 2
What's in the block doesn't matter, in this case. It's just being used as a carrier for the binding.
Other ways to achieve the same goal
Yield an object
A more pedestrian way for a block to interact with it's caller is to pass an object to the block:
class Point
attr_accessor :x
attr_accessor :y
end
class Location
def set
point = Point.new
yield point
p point.x # => 10
p point.y # => 20
end
end
location = Location.new
location.set do |point|
point.x = 10
point.y = 20
end
This is often preferred to fancier techniques: It's easy to understand both its implementation and its use.
instance_eval an object
If you want to (but you probably shouldn't want to), the block's caller can use instance_eval/instance_exec to call the block. This sets self to the object, for that block.
class Location
def set(&block)
point = Point.new
point.instance_eval(&block)
p point.x # => 10
p point.y # => 20
end
end
location = Location.new
location.set do
self.x = 10
self.y = 20
end
You see that the block had to use use self. when calling the writers, otherwise new, local variables would have been declared, which is not what is wanted here.
Yield or instance_eval an object
Even though you still probably shouldn't use instance_eval, sometimes it's useful. You don't always know when it's good, though, so let's let the method's caller decide which technique to use. All the method has to do is to check that the block has parameters:
class Location
def set(&block)
point = Point.new
if block.arity == 1
block.call point
else
point.instance_eval(&block)
end
p point.x
p point.y
end
end
Now the user can have the block executed in the scope of the point:
location = Location.new
location.set do
self.x = 10
self.y = 20
end
# => 10
# => 20
or it can have the point passed to it:
location.set do |point|
point.x = 30
point.y = 40
end
# => 30
# => 40

question on overriding + operator in ruby

A recent convert to Ruby here. The following question isn't really practical; it's more of a question on how the internals of Ruby works. Is it possible to override the standard addition operator to accept multiple inputs? I'm assuming that the answer is no, given that the addition operator is a standard one, but i wanted to make sure i wasn't missing something.
Below is the code I wrote up quick to verify my thoughts. Note, it's completely trivial/contrived.
class Point
attr_accessor :x, :y
def initialize(x,y)
#x, #y = x, y
end
def +(x,y)
#x += x
#y += y
end
def to_s
"(#{#x}, #{#y})"
end
end
pt1 = Point.new(0,0)
pt1 + (1,1) # syntax error, unexpected ',', expecting ')'
You should not mutate the object when implementing + operator. Instead return a new Point Object:
class Point
attr_accessor :x, :y
def initialize(x,y)
#x, #y = x, y
end
def +(other)
Point.new(#x + other.x, #y + other.y)
end
def to_s
"(#{#x}, #{#y})"
end
end
ruby-1.8.7-p302:
> p1 = Point.new(1,2)
=> #<Point:0x10031f870 #y=2, #x=1>
> p2 = Point.new(3, 4)
=> #<Point:0x1001bb718 #y=4, #x=3>
> p1 + p2
=> #<Point:0x1001a44c8 #y=6, #x=4>
> p3 = p1 + p2
=> #<Point:0x1001911e8 #y=6, #x=4>
> p3
=> #<Point:0x1001911e8 #y=6, #x=4>
> p1 += p2
=> #<Point:0x1001877b0 #y=6, #x=4>
> p1
=> #<Point:0x1001877b0 #y=6, #x=4>
You can define the + method like that, but you'll only be able to call it using normal method call syntax:
pt1.+(1,1)
You can achieve something similar using arrays:
def +(args)
x, y = args
#x += x
#y += y
end
and later use it as:
pt1 + [1, 1]
You can also combine it with Chandra's solution, to accept both arrays and Points as arguments.

Resources