class Account
attr_accessor :balance
def initialize (amount=0)
self.balance = amount
end
def -(x)
self.balance -= x
end
def +(x)
self.balance += x
end
def to_s
puts "Balance is #{self.balance} !!!!!!!!!"
end
end
acc = Account.new(20)
acc -=5
Can someone explain why it works to write: puts acc (render 15 as a result) but this line:
puts acc.balance doesn't work (it says undefined method balance, however it's not a method, it should be seen as a property/attribute)
Thanks in advance
acc -= 5 is equivalent to acc = acc - 5.
acc - 5 is 15 (by Account#- method); acc = acc - 5 is like acc = 15.
Now acc is Fixnum object.
How about implement +, - as follow?
def -(x)
Account.new(self.balance - x)
end
def +(x)
Account.new(self.balance + x)
end
The source of your error is line
acc -= 5
it assings result (returned value) of substraction method to acc variable, and this result is Fixnum instance, which doesn't have balance method defined.
If you want your + and - methods to return Account instance, you need to specify this:
def +(x)
self.balance += x
self
end
etc.
BTW, accessors in Ruby ARE methods.
You want to edit your two methods to make += and -= work, so that they return the object in place:
def -(x)
self.balance -= x
self
end
def +(x)
self.balance += x
self
end
That way, the object it reassigned to itself.
Oops, there is a number of things that have been wrong with this one. I cannot explain all the problems, but let's see how far I can get:
First of all you have redefined the #+ and #- methods to give them semantics you would expect from #+= and #-=. Given this: if you use acc-5 in your example (instead of acc-=5) the behavior is close to what you expect, irb will display 15 as this is the result of your #- method (which has the side effect of subtracting 5 of balance as well).
However, as you call #-= in your example the ruby interpreter ends up assigning the methods result Fixnum 15 to your acc-variable. This will give you the error message NoMethodError: undefined method 'balance' for 15:Fixnum when you try to access #balance which happens to be defined on your Account class.
Your #- method should return an Account-object, not change the existing state of the object and return the result as a Fixnum. See http://ruby.about.com/od/oo/ss/Overloading-Operators.htm for examples on how to redefine operators. I understand that this somehow defeats your purpose as you would have liked to use #-= to modify the object in place, but it only works by creating another Account-instance in Ruby as you are not able to redefine #-= directly (see the link above).
Of course you are free to define a #withdraw-method to get the semantics you desire.
Related
I'm reading the "Pickaxe" ruby book and I came across this example:
def meth_three
100.times do |num|
square = num*num
return num, square if square > 1000
end
end
If you call meth_three in irb it returns the first integer between 1 and 100 that has a square > 1000:
meth_three # => [32, 1024]
My question is, how does the times method know how to loop through each integer between 1..100 to pass as the argument to the |num| parameter?
There are many, many different ways of writing iteration in Ruby.
Here is one possible way that it could be implemented recursively:
class Integer
def times(&blk)
return enum_for(__callee__) unless block_given?
return self unless positive?
pred.times(&blk)
yield pred
end
end
Or, using Range#each:
class Integer
def times(&blk)
return enum_for(__callee__) unless block_given?
return self unless positive?
(0...self).each(&blk)
self
end
end
Or, with a loop:
class Integer
def times
return enum_for(__callee__) unless block_given?
return self unless positive?
i = -1
yield i while (i += 1) < self
self
end
end
And a tail-recursive implementation just for the fun of it:
class Integer
def times(&blk)
return enum_for(__callee__) unless block_given?
return self unless positive?
__times_rec(0, &blk)
self
end
private def __times_rec(i, &blk)
return unless i < self
yield i
__times_rec(i.succ, &blk)
end
end
Here is the actual code from an actual Ruby implementation (core/integer.rb from Rubinius):
def times
return to_enum(:times) { self } unless block_given?
i = 0
while i < self
yield i
i += 1
end
self
end
TruffleRuby uses the exact same code as well (see src/main/ruby/truffleruby/core/integer.rb), as does JRuby (see core/src/main/ruby/jruby/ruby_implementations/Integer/times.rb)
Here is another real example from a real Ruby implementation (opal/corelib/number.rb from Opal):
def times(&block)
return enum_for(:times) { self } unless block
%x{
for (var i = 0; i < self; i++) {
block(i);
}
}
self
end
As you can see, there are many ways in which one could write a loop in Ruby.
Some of the comments hint at what is going on. I think it can be explained like this:
Each integer in Ruby is actually an object, more specifically an instance of the Integer class.
You can think of it as a Integer#times method, that could naively be implemented something like this:
class Integer
def times(&block)
if self.value > 0
for i in 0...self.value do
block.call(i)
end
end
end
end
This method is actually implemented in C in the official version of ruby (MRI), as pointed out in one of the comments to your question. I only write something similar here to help explain the concept on how it could have looked in Ruby.
The times method actually returns a value, which is the current iterator with every loop it makes (1, 2, 3 ... 100).
The do keyword catches this iterator, and uses it as the variable num.
Anytime you see the num variable, you are seeing the current iterator that's being returned by times.
I am creating an Elevator object with an instance that can only go between floors one and twelve. The code works for the up and down instance methods, but I cannot get the elevator to not go above floor 12 or below floor 1. I tried using unless #floor >= 12, but there was a syntax error. I'm sure it is simple, but I am new to Ruby.
Here is the code that works:
class Elevator
##count = #floor
#The Constructor Method
def initialize(floor) #floor is an instance variable of the Elevator object.
#floor = floor
cheery_greeting
end
def cheery_greeting
puts "Hello my friend! would you like to go up or down?"
end
def self.notify()
"You are now on floor #{##count}"
end
#accessor methods
def go_up
#floor += 1
notify
end
def go_down
#floor -= 1
notify
end
I want to add a break so that it stops iterating when we reach floor twelve, so I wrote this, but it wouldn't even puts.
def floor_limit
if ##count == 12
puts "You're Way Too High!"
end
I also tried:
def go_up
unless #floor >= 12
#floor += 1
notify
end
You're mixing class instance variables with instance variables here and that's going to lead to trouble and confusion. If you're new to Ruby I strongly advise you to avoid using class instance variables, they just lead to a lot of mess. Instead focus on making each instance as self-contained as possible.
To make this more Ruby you can do a few things:
class Elevator
# Define a valid range for this elevator
FLOOR_RANGE_DEFAULT = (1..12)
# Expose the current floor and range as properties
attr_reader :floor
attr_reader :range
def initialize(floor, range = nil)
#floor = floor.to_i
#range = (range || FLOOR_RANGE_DEFAULT).to_a
end
def inspect
#range.map do |floor|
if (#floor == floor)
'[%d]' % floor
else
' %d ' % floor
end
end.join(' ')
end
end
Then your up and down code can check limits and reject if that's an invalid operation. First separate the moving code from the code that interprets up or down:
def up
move(#floor + 1)
end
def down
move(#floor - 1)
end
def move(target)
if (#range.include?(target))
#floor = target
end
#floor
end
Now you have a framework that you can build on. By using simple things like Ruby's range feature you can make a very adaptable class that can handle situations like elevators that have other limits:
e = Elevator.new(1, (1..20))
Or that stop only on odd floors:
e = Elevator.new(1, (1..20).select(&:odd?))
Or skip the 4th, 13th and 14th:
e = Elevator.new(1, (1..20).to_a - [ 4, 13, 14 ])
It doesn't take more code, it just takes the right code.
The following should do the trick.
def go_up
#floor += 1 unless #floor >= 12
notify
end
Or like this:
def go_up
#floor += 1 if #floor < 12
notify
end
They are both pretty intuitive so it's up to you.
This is a pretty detailed explanation on when to use unless.
And here is a standard if/else/unless tutorial.
EDIT: For those criticizing my intentions with replacing self, you are free to click the back button, continue developing with your own opinions, and leave me to develop with mine :)
I was wondering if there is a way to completely remove the object that self references and replace it with a new instance.
Example:
def refresh_from_server!
self = newly_fetched_object_from_server
end
I don't want to return the new object.
It seems like I would have to build my own copying interface and call self.copy_from(other_object) but maybe someone has a cool ruby bit to share that works better!
--EDIT
Since some people seem unclear on the question, I want instance.my_method! to completely replace instance with a new instance of that class
For example lets imagine we have a class
class Counter
attr_accessor :count
def initialize
count = 0
end
def reset!
# This is what I want to achieve.
# Obviously in this case it would be trivial to write `self.count = 0`
# Or return a new value
# But I have a much more complex object in real life
# which copying would not be trivial
# All I'm looking for is a bit of stylistic sugar to make my code look cooler
# If it doesn't exist, I would love to know why
self = Counter.new
end
def up
count += 1
end
end
No, you can't replace self. You can only change some/all of its state, but the object reference will remain the same.
Why would you want to do this, anyway? If you just want to piggyback on your initialization logic (as it seems to me to be the case), some refactoring will help: just call a shared method from both places.
class Counter
attr_accessor :count
def initialize
init_state
end
def reset!
init_state
end
def up
self.count += 1
end
private
def init_state
self.count = 0
end
end
As already noted by others, self can't be replaced from enclosed instance. If replacement of instance with a new instance is required, it need to be done from outside, like in a class factory which registers its class instances.
Bellow is a simplest example using a delegator, demonstrating what I mean. SimpleDelegator represents a simple wrapper around Counter instance:
require 'delegate'
class Counter
attr_accessor :count
def initialize
#count = 0
end
end
class CounterDecorator < SimpleDelegator
def reset!
__setobj__(__getobj__.class.new)
end
end
c = CounterDecorator.new(Counter.new)
p c.__getobj__.object_id
c.count = 123
p c.count
c.reset!
p c.__getobj__.object_id
p c.count
# produces following output
20131160
123
20130900
0
Though the question is old, it is still visited. I will attempt to elaborate more on the "why" in "Why can't self be replaced in Ruby?".
usage of self in which context
https://web.archive.org/web/20191217060940/https://www.honeybadger.io/blog/ruby-self-cheat-sheet/
There are various contexts in which self can be used. You question uses it in the context of an instance method, so I will focus on that.
E.g. this context:
class SomeClass
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
a = SomeClass.new
a.some_method
# prints : SomeClass - 47013616336320 - #<SomeClass:0x000055846bcd7b80>
Note that there are other usages of self: e.g. where it reference the Class object in scope of a class definition. E.g.
class SomeClass
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
# prints : Class - 47102719314940 - SomeClass
the intended effect of replacing self
Below code a demonstration of what you expected / wished (as I understand it):
class Counter
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
def refresh!
self = Counter.new # not possible
# results in error : "Can't change the value of self"
end
end
a = Counter.new
a.some_method
# prints : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish a to point to a different object
But what about other references? E.g. assuming you wanted:
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish both a and b to point to the same (new) object
If stated as such it gives a hint on the why not.
why we can't replace self
The short answer is that it is simply not something that the language / interpreter offers. As to the reasoning: in a way #matthewd answers that in this answer:
All ruby variable references are essentially pointers (but not
pointers-to-pointers), in C parlance.
You can mutate an object (assuming it's not immutable), and all
variables that reference it will thus be pointing at the same (now
mutated) object. But the only way to change which object a variable is
referring to is with direct assignment to that variable -- and each
variable is a separate reference; you can't alias a single reference
with two names.
In short: there may be other references to that object in variables that are not in the scope of the instance method. These cannot be manipulated by that instance method.
a way to achieve the intended effect
If you want this effect and only want to touch the code of Counter you might move all methods and state to an inner class Counter::Inner and make Counter behave like a decoupled reference. The only 'state' of Counter would be the reference to the Counter::Inner object and Counter can delegate all calls it receives to that reference in a method_missing method. In case of your refresh! you can replace the reference in Counter same as you now intent to replace self. All outside code will now use indirectly the new Counter:Inner instance.
class Counter
class Inner
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
def initialize(*args)
#reference = Inner.new(*args)
end
def method_missing(method_id, *args)
#reference.send(method_id, *args)
end
def refresh!
#reference = Inner.new
end
end
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238242100 - #<Counter::Inner:0x0000557a00203e68>
a.refresh!
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238240000 - #<Counter::Inner:0x0000557a00202e00>
Just one more answer for the archives :-) I hope this gives useful insights to future visitors.
I want to define a custom :+= method on my Ruby class that modifies the instance. However, the following code raises syntax error, unexpected '=', expecting ';' or '\n'
def +=(other)
#value += other.to_f
end
What are my options here? I see that Fixnum has :+# and :-# methods, but I don't exactly see what they do in the documentation. Are those the methods I want to write?
Unlike C++ or other languages with robust overrides, there's no such method as += in Ruby.
What's happening internally is a sort of expansion. For example:
x += n
If x is a variable, then this is equivalent to:
x = x.send(:+, n)
If x= is defined, then this is equivalent to:
send(:x=, x.send(:+, n))
Whatever you need to do to override must be to redefine x= or + on the class of x.
Remember that the + method should not modify x. It's supposed to return a copy of x with the modification applied. It should also return an object of the same class of x for consistency.
Your method should look like:
def +(other)
# Create a new object that's identical in class, passing in any arguments
# to the constructor to facilitate this.
result = self.class.new(...)
result.value += other.to+f
result
end
I am not really sure, but you do not need the = operator.
def +(other)
#value += other.to_f
end
Example from here:
class Money
attr_accessor :value
def +(b)
result = dup
result.value += b.value
return result
end
def -(b)
result = dup
result.value += b.value
return result
end
end
I have this code:
l = lambda { a }
def some_function
a = 1
end
I just want to access a by the lambda and a special scope which has defined a already somewhere like inside some_function in the example, or just soon later in the same scope as:
l = lambda { a }
a = 1
l.call
Then I found when calling l, it is still using its own binding but not the new one where it was called.
And then I tried to use it as:
l.instance_eval do
a = 1
call
end
But this also failed, it is strange that I can't explain why.
I know the one of the solution is using eval, in which I could special a binding and executing some code in text, but I really do not want to use as so.
And, I know it is able to use a global variable or instance variable. However, actually my code is in a deeper embedded environment, so I don't want to break the completed parts if not quite necessary.
I have referred the Proc class in the documentation, and I found a function names binding that referred to the Proc's context. While the function only provided a way to access its binding but cannot change it, except using Binding#eval. It evaluate text also, which is exactly what I don't like to do.
Now the question is, do I have a better (or more elegant) way to implement this? Or using eval is already the regular manner?
Edit to reply to #Andrew:
Okay, this is a problem which I met when I'm writing a lexical parser, in which I defined a array with fixed-number of items, there including at least a Proc and a regular expression. My purpose is to matching the regular expressions and execute the Procs under my special scope, where the Proce will involved some local variables that should be defined later. And then I met the problem above.
Actually I suppose it is not same completely to that question, as mine is how to pass in binding to a Proc rather than how to pass it out.
#Niklas:
Got your answer, I think that is what exactly I want. It has solved my problem perfectly.
You can try the following hack:
class Proc
def call_with_vars(vars, *args)
Struct.new(*vars.keys).new(*vars.values).instance_exec(*args, &self)
end
end
To be used like this:
irb(main):001:0* lambda { foo }.call_with_vars(:foo => 3)
=> 3
irb(main):002:0> lambda { |a| foo + a }.call_with_vars({:foo => 3}, 1)
=> 4
This is not a very general solution, though. It would be better if we could give it Binding instance instead of a Hash and do the following:
l = lambda { |a| foo + a }
foo = 3
l.call_with_binding(binding, 1) # => 4
Using the following, more complex hack, this exact behaviour can be achieved:
class LookupStack
def initialize(bindings = [])
#bindings = bindings
end
def method_missing(m, *args)
#bindings.reverse_each do |bind|
begin
method = eval("method(%s)" % m.inspect, bind)
rescue NameError
else
return method.call(*args)
end
begin
value = eval(m.to_s, bind)
return value
rescue NameError
end
end
raise NoMethodError
end
def push_binding(bind)
#bindings.push bind
end
def push_instance(obj)
#bindings.push obj.instance_eval { binding }
end
def push_hash(vars)
push_instance Struct.new(*vars.keys).new(*vars.values)
end
def run_proc(p, *args)
instance_exec(*args, &p)
end
end
class Proc
def call_with_binding(bind, *args)
LookupStack.new([bind]).run_proc(self, *args)
end
end
Basically we define ourselves a manual name lookup stack and instance_exec our proc against it. This is a very flexible mechanism. It not only enables the implementation of call_with_binding, it can also be used to build up much more complex lookup chains:
l = lambda { |a| local + func(2) + some_method(1) + var + a }
local = 1
def func(x) x end
class Foo < Struct.new(:add)
def some_method(x) x + add end
end
stack = LookupStack.new
stack.push_binding(binding)
stack.push_instance(Foo.new(2))
stack.push_hash(:var => 4)
p stack.run_proc(l, 5)
This prints 15, as expected :)
UPDATE: Code is now also available at Github. I use this for one my projects too now.
class Proc
def call_with_obj(obj, *args)
m = nil
p = self
Object.class_eval do
define_method :a_temp_method_name, &p
m = instance_method :a_temp_method_name; remove_method :a_temp_method_name
end
m.bind(obj).call(*args)
end
end
And then use it as:
class Foo
def bar
"bar"
end
end
p = Proc.new { bar }
bar = "baz"
p.call_with_obj(self) # => baz
p.call_with_obj(Foo.new) # => bar
Perhaps you don't actually need to define a later, but instead only need to set it later.
Or (as below), perhaps you don't actually need a to be a local variable (which itself references an array). Instead, perhaps you can usefully employ a class variable, such as ##a. This works for me, by printing "1":
class SomeClass
def l
#l ||= lambda { puts ##a }
end
def some_function
##a = 1
l.call
end
end
SomeClass.new.some_function
a similar way:
class Context
attr_reader :_previous, :_arguments
def initialize(_previous, _arguments)
#_previous = _previous
#_arguments = _arguments
end
end
def _code_def(_previous, _arguments = [], &_block)
define_method("_code_#{_previous}") do |_method_previous, _method_arguments = []|
Context.new(_method_previous, _method_arguments).instance_eval(&_block)
end
end
_code_def('something') do
puts _previous
puts _arguments
end