Assume, I have an object x of MyClass. What method is called, when I do puts x? I need to override it with my own one.
I thought it was .inspect, but somehow overridden inspect isn't being called.
For example, I have a class Sum:
class Sum
initiazlie a, b
#x = a + b
end
end
And I wanna access the result like this:
s = Sum.new(3,4)
puts s #=> 7 How do I do this?
puts 10 + s #=> 17 or even this...?
It calls: to_s
class Sum
def to_s
"foobar"
end
end
puts Sum.new #=> 'foobar'
Or if you want, you can just call inspect from to_s, so that you have a consistent string representation of your object.
class Sum
def to_s
inspect
end
end
First, your Sum class is invalid. The definition should be.
class Sum
def initialize(a, b)
#x = a + b
end
end
By default, the method called to get a human readable representation is inspect. Try this in irb
$ s = Sum.new(3, 4)
# => #<Sum:0x10041e7a8 #x=7>
$ s.inspect
# => "#<Sum:0x10041e7a8 #x=7>"
But in your case, you use the puts method which forces a string conversion. For this reason, the Sum object is first converted to string using the to_s method.
$ s = Sum.new(3, 4)
# => #<Sum:0x10041e7a8 #x=7>
$ puts s
# => #<Sum:0x10041e7a8>
$ puts s.to_s
# => #<Sum:0x10041e7a8>
Also note your last example fall into a third case. Because you sum a Fixnum + an other object, the result is expected to be a Fixnum and the method called is to_s but the one defined in the Fixnum class.
In order to use the one in your Sum class, you need to switch the items in the sum and define the + in your object.
class Sum
def initialize(a, b)
#x = a + b
end
def +(other)
#x + other
end
def to_s
#x.to_s
end
end
s = Sum.new(3, 4)
s + 10
puts s
# => 17
Related
hey I want that my method logify puts each method with its parameters and return value of my class A. I wrote for example a simple class A with two methods add and sub and the output should look like that:
Output:
Method add(1, 2) called
return value 3
Method sub(1, 2) called
return value -1
I know that I can get each method with self.instance_methods(false) but can someone please help me further?
require_relative "log"
class A
extend Log
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
logify
end
a = A.new
a.add(2,1)
a.sub(2,1)
module Log
def logify
puts self.instance_methods(false)
end
end
You can use Module#prepend and Module#prepended to help with this like so:
module Log
def self.prepended(base)
base.instance_methods(false).each do |m|
define_method(m) do |*args, &block|
puts "Method #{m}(#{args.join(',')}) called"
val = super(*args, &block)
puts "return value #{val}"
val
end
end
end
end
class A
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
end
A.prepend(Log)
What this does is it defines a method in the prepended module with the same name as the original then builds your output and delagets to the original method in the middle (super) to obtain the return value.
Examples
a = A.new
a.add(2,1)
# Method add(2,1) called
# return value 3
#=> 3
a.sub(2,1)
# Method sub(2,1) called
# return value 1
#=> 1
Caveat: this will only show the provided arguments and will not output default arguments in the method signature
The ruby core library includes the class TracePoint, which can be used to trace just about anything - from methods being defined, or invoked, or exceptions being raised, ...
Here is an example usage, which will perform the tracking you desired:
class A
def add(a, b)
a + b
end
def sub(a, b)
a - b
end
end
TracePoint.trace(:call, :return) do |tp|
next unless tp.defined_class == A
case tp.event
when :call
params = tp.parameters.map { |arg| eval(arg[1].to_s, tp.binding) }
puts "Method #{tp.method_id}(#{params.join(', ')}) called"
when :return
puts "return value #{tp.return_value}"
end
end
# The trace has been enabled! Any time one of those events occurs, the block is evaluated.
a = A.new
a.add(2,1)
a.sub(2,1)
Output:
Method add(2, 1) called
return value 3
Method sub(2, 1) called
return value 1
Fetching the params data is, as you can see, a little troublesome. TracePoint has access to the method signature, but you need to make use of the trace's binding to see what values it's actually been called with.
I'm writing a Reverse Polish Notation (RPN) calculator which, until now, looks like this:
class RPNCalculator
attr_accessor :numbers, :value
def initialize
#numbers = []
#value = nil
end
def push(number)
#numbers.push(number)
end
def plus
if value.nil?
number1=#numbers.pop
number2=#numbers.pop
#value = number1 + number2
else
#value += #numbers.pop
end
end
# minus_proc = Proc.new {|a,b| a - b}
def minus
number1=#numbers.pop
number2=#numbers.pop
#value = number2 - number1
# operate_on_two_numbers(minus_proc)
end
def operate_on_two_numbers(&prc)
number1=#numbers.pop
number2=#numbers.pop
#value = prc.call(number1,number2)
end
end
For example, the following commands
calculator=RPNCalculator.new
calculator.push(5)
calculator.push(8)
calculator.plus
p calculator.value
produce the number "13".
This is how it's supposed to work, but there seems to be a lot of repetition in the code. For example, the "plus" and "minus" functions are largely the same; only a "+" and "-" inside the code differs.
I'd like to try to define a (private) method which accepts as an argument an operation which operates on two numbers, such as "+", "-", "*", or "/". It would be nice to use 'shortcut notation' like the shortcut .inject(:+).
I've given it a try with the commented-out lines and the method operate_on_two_numbers, but when I comment in the lines I get an error "undefined local variable or method 'minus_proc'".
You could use any arithmetic operator here in the same manner
def operate_on_two_numbers(a, b, op)
a.send(op, b)
end
operate_on_two_numbers(1, 2, :+)
#=> 3
operate_on_two_numbers(1, 2, :-)
#=> -1
Here's what your full code could look like
class RPNCalculator
attr_accessor :numbers, :value
def initialize
#numbers = []
#value = nil
end
def push(number)
#numbers.push(number)
end
def plus
operate_on_two_numbers(:+)
end
def minus
operate_on_two_numbers(:-)
end
private
def operate_on_two_numbers(op)
number1=#numbers.pop
number2=#numbers.pop
#value = number1.send(op, number2)
end
end
calculator=RPNCalculator.new
#=> #<RPNCalculator:0x00000002efe460 #numbers=[], #value=nil>
calculator.push(5)
#=> [5]
calculator.push(8)
#=> [5, 8]
calculator.plus
#=> 13
calculator.value
#=> 13
Although I'll accept the ".send" method as what I was looking for initially, in the end I used a method which accepts a block as an argument, because for division (the "divide" method) I required some more operations (namely, converting to float prior to division). The updated RPN calculator is shown below.
class RPNCalculator
attr_accessor :numbers, :value
def initialize
#numbers = []
#value = nil
end
def push(number)
#numbers.push(number)
end
def plus
operate_on_two_numbers {|a,b| a + b}
end
def minus
operate_on_two_numbers {|a,b| b - a}
end
def times
operate_on_two_numbers {|a,b| a * b}
end
def divide
operate_on_two_numbers {|a,b| Float(b)/Float(a)}
end
private
def operate_on_two_numbers(&prc)
if #numbers.empty?
raise "calculator is empty"
else
number1=#numbers.pop
if #value.nil?
number2=#numbers.pop
#value = prc.call(number1,number2)
else
#value = prc.call(number1,#value)
end
end
end
end
Note that "plus", "minus", etc. are now one-line methods, and much of the complexity has been moved into the private "operate_on_two_numbers" method.
We were asked to create geometric shapes via classes. We must create:
A Point class.
A Line class (line - when two points are connected).
A square class.
I created a Point class, with accessible coordinates (2D):
class Point
attr_accessor :x, :y
def initialize
#x = 10
#y = 10
end
def x=(value)
#x = value
end
def x()
#x
end
def y=(value)
#y = value
end
def y()
#y
end
end
And, I want to create a new point class:
p = Point.new
p.x = 1
p.y = 5
print p # -> #<Point:0x007f9463089cc0>
And, as a result, I have some thing like this:
#<Point:0x007f9463089cc0>
What does this mean?
If I print p.x, p.y then I will have a understandable result:
print p.x, ", ", p.y # -> 1, 5
How can I understand this result on the screen?
Is there any use in a real programming job to create point, lines and geometric shapes?
You don't need to write the setters and getters methods:
def x=(value)
#x = value
end
def x()
#x
end
def y=(value)
#y = value
end
def y()
#y
end
This is because you can use:
attr_accessor :x, :y
and attr_accessor does that for you.
You might want to allow some flexibility in your constructor. Your initialize method allows passing the values for x and y and will default to 10 if nothing is passed, so you can do this:
def initialize(x = 10, y = 10)
#x = x
#y = y
end
This way, you will get this:
p1 = Point.new
puts p.x # => 10
puts p.y # => 10
p2 = Point.new(15, 20)
puts p.x # => 15
puts p.y # => 20
Notice how for p1 I don't pass any arguments and yet x and y both get set as expected because we are setting a default value for them in the method definition, here:
def initialize(x = 10, y = 10)
Regarding your question about why you see this:
p = Point.new
p.x = 1
p.y = 5
print p # -> #<Point:0x007f9463089cc0>
Point:0x007fa003885bf8 means that you have an instance of the class Point in your variable p. By default Ruby will call the to_s method on an object when you try to print it, since in your case you didn't define that method it will go through the inheritance chain to see who defines that method. It turns out that that method is found in the Object class which all Ruby objects implicitly inherit from, and that method's default behaviour is to print the name of the class followed by the instance's ID in memory, in the format:
#<ClassName:MemoryID>
See: http://ruby-doc.org/core-2.3.1/Object.html#method-i-to_s
If you want to change that then you can override to_s to something like this:
def to_s
"Point #{x},#{y}"
end
That way you will get:
puts Point.new # => "Point 10,10"
There's nothing wrong.
#<Point:0x007f9463089cc0>
simply means that it is an instance (the #<> part) of class Point with an object ID of 0x007f9463089cc0. An object ID is an identifier for Ruby's interpreter to find each object, very much like a memory address.
Though everything is okay, there is always a way in Ruby that leads to less code. For example:
class Point
attr_accessor :x, :y
def initialize(x = 10, y = 10)
#x, #y = x, y
end
end
Or even:
Point = Struct.new(:x, :y)
Methods print and puts output the result of to_s, and p outputs the result of inspect. Unless you overwrite these methods, by default, a custom class that inherits Object returns the form #<class_name:object_id> that you got. If you want some other output, overwrite these methods, for example
class Point
def to_s
"##x,##y"
end
end
...
print p
Because you are declaring attr_accessor :x, :y
you actually don't need to define a getter and setter for x and y,
so your class can be simplified to:
class Point
attr_accessor :x, :y
def initialize
#x = 10
#y = 10
end
end
Also you can add a to_s method to Point:
class Point
attr_accessor :x, :y
def initialize
#x = 10
#y = 10
end
def to_s
"(#{x}, #{y})"
end
end
so you can use puts p and get as ouptut (10, 10) when you do:
p = Point.new
puts p
(10, 10)
I prefer to use puts instead of print because it insert a new-line character after the output, and it looks more readable.
In to_s I'm using Ruby's string interpolation "#{}" to build a nice output for your Point instance.
I thought that defining a to_s method for a custom class meant that calling the puts method on that class would return an output as specified by to_s. In this program, however, I only get the result I crave if I write puts bingo_board.to_s. What is going on?
class BingoBoard < Array
##letters = %w[B I N G O]
def initialize
# populates an 5x5 array with numbers 1-100
# to make this accessible across your methods within this class, I made
# this an instance variable. # = instance variable
#bingo_board = Array.new(5) {Array.new(5)}
#bingo_board.each_with_index do |column, i|
rangemin = 15 * i + 1
#bingo_board[i] = (rangemin..(rangemin+14)).to_a.sample(5)
end
#bingo_board[2][2] = "X" # the 'free space' in the middle
#game_over = false
end
def game_over?
#game_over
end
def generate_call
....
end
def compare_call(call)
#bingo_board[##letters.index(call[0])].include? call[1]
end
def react_to_call(call)
...
end
def check_board
...
end
def show_column(num)
...
end
def to_s
result = ""
0.upto(4) do |val|
result += " " + ##letters[val] + " "
end
result += "\n\n"
0.upto(4) do |row|
0.upto(4) do |col|
val = #bingo_board[col][row]
result += " " if val.to_i < 10
result += val.to_s + " "
end
result += "\n"
end
result
end
end
my_board = BingoBoard.new
counter = 0
until my_board.game_over?
puts my_board.to_s # renders the board in accordance with my to_s method
call = my_board.generate_call
counter += 1
puts "\nThe call \# #{counter} is #{call[0]} #{call[1]}"
my_board.react_to_call(call)
gets.chomp
end
puts my_board # renders bubkes (i.e., nothing)
puts "\n\n"
puts "Game over"
Its because you'r extending from Array. That's why you're getting the wierd behavior. I don't see where you need the extending from so just remove that and things will work as you expect.
Here's a more detaled answer if you'd like to know why this is happening. Basically puts makes an exception for arrays so when an array is passed puts is called on each member. Ruby Array#puts not using overridden implementation?
As #jörgwmittag said, this is a special case. The IO#puts method treats arrays - which means anything that responds to to_ary - differently. It calls to_ary first and then iterates over each element of the resulting array, and only calls to_s on them. It never calls to_s on the array itself.
If you delegate to a member array instead of subclassing from Array, you have finer-grained control over what gets "inherited" (delegated). Then you can exclude to_ary from the delegation, which will prevent puts from seeing your object as an Array and triggering this behavior.
Other general solutions:
Use string interpolation or explicit to_s calls so that what puts receives is already a string:
puts "#{bingo_board}"
puts bingo_board.to_s
Use print or printf instead of puts:
print bingo_board,"\n"
printf "%s\n",bingo_board
If the object is an Array or can be converted to one (i.e. it implements to_ary), then puts will not call to_s on the object, but rather iterate over the object and print each object within by calling to_s on it.
See:
puts [1, 2]
# 1
# 2
[1, 2].to_s
# => '[1, 2]'
This is actually documented, although somewhat implicitly:
If called with an array argument, writes each element on a new line.
Looks like it runs Array#inspect method instead of your custom to_s. Adding alias_method :inspect, :to_s just after ending of the to_s definition will help you.
But it'll work only with p, because puts runs each(&:inspect).
Why can you chain this:
"Test".upcase.reverse.next.swapcase
but not this:
x = My_Class.new
x.a.b.c
where
class My_Class
def a
#b = 1
end
def b
#b = #b + 2
end
def c
#b = #b -72
end
end
The upcase, reverse, next and swapcase methods all return String objects and all those methods are for... you guessed it, String objects!
When you call a method (more often than not, like 99.9999% of the time) it returns an object. This object has methods defined on it which can then be called which explains why you can do this:
"Test".upcase.reverse.next.swapcase
You can even call reverse as many times as you like:
"Test".reverse.reverse.reverse.reverse.reverse.reverse.reverse.reverse
All because it returns the same kind of object, a String object!
But you can't do this with your MyClass:
x = My_Class.new
x.a.b.c
For that to work, the a method would have to return an object which has the b method defined on it. Right now, that seems like only instances of MyClass would have that. To get this to work you could make the return value of a the object itself, like this:
def a
#b += 2
self
end
Extrapolating this, the b method would also need to return self as the c method is available only on instances of the MyClass class. It's not important what c returns in this example, because it's the end of the chain. It could return self, it could not. Schrödinger's cat method. Nobody knows until we open the box.
As support for other answers, this code:
"Test".upcase.reverse.next.swapcase
...is almost exactly the same as...
a = "Test"
b = a.upcase
c = b.reverse
d = c.next
e = d.swapcase
....except that my code above has extra variables left over pointing to the intermediary results, whereas the original leaves no extra references around. If we do this with your code:
x = MyClass.new # x is an instance of MyClass
y = x.a # y is 1, the last expression in the a method
z = y.b # Error: Fixnums have no method named 'b'
Using Ruby 1.9's tap method, we can even make this more explicit:
irb> "Test".upcase.tap{|o| p o}.reverse.tap{|o| p o}.next.tap{|o| p o}.swapcase
#=> "TEST"
#=> "TSET"
#=> "TSEU"
=> "tseu"
irb> class MyClass
irb> def a
irb> #b = 1
irb> end
irb> def b
irb> #b += 2
irb> end
irb> end
=> nil
irb(main):011:0> x = MyClass.new
=> #<MyClass:0x000001010202e0>
irb> x.a.tap{|o| p o}.b.tap{|o| p o}.c
#=> 1
NoMethodError: undefined method `b' for 1:Fixnum
from (irb):12
from /usr/local/bin/irb:12:in `<main>'
The last expression in a function is its implicit return value. You need to return self if you want to chain methods like that.
For example, your a method is currently returning 1. b is not a method for numbers. You'll want to modify it like so:
def a
#b = 1
self
end