Forum: Ruby Adding an Increment Operator?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Dave H. (Guest)
on 2006-06-02 03:18
(Received via mailing list)
As Logan pointed out in a different thread:

>
> % ruby increment.rb
> -:13: Can't change the value of self
>     self = self + 1
>           ^

So, how WOULD I create an .increment operator? I get why I can't put it
in class Fixnum (well, I think I know why); Fixnums are symbol-ish-ly
unalterable. 5.increment should fail, for sanity's sake.

So I have to add .increment to class . . . hmm. I can't add it to
variables; they're just references. But I want my_var.increment to
change 'my_var' so that it references the next integer in line.

Strings have methods that alter strings in place, but a String object
is a composite object, made up of characters, and !-type methods alter
the characters of the string without altering the variable's reference.
Numbers aren't composite objects. Maybe it can't be done? But people
are constantly amazing me with crazy magical Ruby tricks; it seems so
improbable that there isn't a way to do this as well . . .

Somebody else pointed out I could just my_var += 1. Yes, but to me,
that implies that the "1" is deliberate, and at some other point I
might add 2 or 5 or something, whereas using "inc" or "++" or whatever
says "it really doesn't matter if I'm counting by ones, tens, Roman
numerals, or letters. This variable is just stepping upwards."

But the main reason I'm asking is not to slightly improve the esthetics
of a couple lines of code. I'm certain that I'm going to learn
something interesting from the answer to the question.
unknown (Guest)
on 2006-06-02 03:46
(Received via mailing list)
On Jun 1, 2006, at 7:15 PM, Dave H. wrote:

>> a.increment
>>
>>
>> % ruby increment.rb
>> -:13: Can't change the value of self
>>     self = self + 1
>>           ^
>
> So, how WOULD I create an .increment operator? I get why I can't
> put it in class Fixnum (well, I think I know why); Fixnums are
> symbol-ish-ly unalterable. 5.increment should fail, for sanity's sake.

Fixnum objects aren't containers for integer values.  Each Fixnum
object represents a different integer via a one-to-one mapping. So
just as you can't increment the integer 1 you can't increment the
Fixnum that represents the integer one.

The concept of 'the next integer' works though and that is available
via Fixnum#succ

Ruby doesn't allow you to use assignment to bind self to a new object
either so
   self = <anything>
is a syntax error.


> So I have to add .increment to class . . . hmm. I can't add it to
> variables; they're just references. But I want my_var.increment to
> change 'my_var' so that it references the next integer in line.

myvar = myvar.succ

Gary W.
Ross B. (Guest)
on 2006-06-02 04:19
(Received via mailing list)
On Fri, 2006-06-02 at 08:15 +0900, Dave H. wrote:
> > a.increment
> >
> >
> > % ruby increment.rb
> > -:13: Can't change the value of self
> >     self = self + 1
> >           ^
>
> So, how WOULD I create an .increment operator? I get why I can't put it
> in class Fixnum (well, I think I know why); Fixnums are symbol-ish-ly
> unalterable. 5.increment should fail, for sanity's sake.

Depending on what you want to do, maybe you could cheat:

require 'delegate'

class Fixbox < DelegateClass(Fixnum)
  def self.[](num); new(num); end
  def decrement; __getobj__ - 1; end
  def decrement!; __setobj__(decrement); end
  def increment; __getobj__ + 1; end
  def increment!; __setobj__(increment); end
end

f = Fixbox[3]
# => 3
f.increment!
# => 4
f
# => 4
f.decrement!
# => 3
f
# => 3

:)
Dave B. (Guest)
on 2006-06-02 04:35
(Received via mailing list)
Dave H. wrote:
> characters of the string without altering the variable's reference.
> But the main reason I'm asking is not to slightly improve the esthetics
> of a couple lines of code. I'm certain that I'm going to learn something
> interesting from the answer to the question.

As has been pointed out, you can't modify a Fixnum, nor can you ever
assign to self.

I wasn't going to mention the kind of generic delegating container Ross
just wrote about; have a look, but I don't think you'll find a place to
use that where there isn't a better solution.

Another approach might be to encapsulate a counter like so:

class Counter
  attr_accessor :value
  def initialize(i = 0)
    @value = i
  end
  def inc
    @value = @value.succ
  end
end

i = Counter.new
i.value #=> 0
i.inc #=> 1
i.inc #=> 2
i.value = "X"
i.inc #=> "Y"

Or you can do like this:

def counter(i = 0)
  proc { i = i.succ }
end

i = counter
i.call #=> 1
i.call #=> 2
i[] #=> 3

Cheers,
Dave
Ross B. (Guest)
on 2006-06-02 14:52
(Received via mailing list)
On Fri, 2006-06-02 at 09:34 +0900, Dave B. wrote:
> Dave H. wrote:
> > So, how WOULD I create an .increment operator? I get why I can't put it
> > in class Fixnum (well, I think I know why); Fixnums are symbol-ish-ly
> > unalterable. 5.increment should fail, for sanity's sake.
> >
> I wasn't going to mention the kind of generic delegating container Ross
> just wrote about; have a look, but I don't think you'll find a place to
> use that where there isn't a better solution.

I have to admit to being at most semi-serious with that suggestion :).
Dave H. (Guest)
on 2006-06-03 02:04
(Received via mailing list)
>> So, how WOULD I create an .increment operator? ... 5.increment should
>> fail, for sanity's sake.

Gary's solution of
	myvar = myvar.succ
is a slight improvement over
	myvar = myvar + 1
but it's still more verbose than I'd hoped to have.

I don't even understand Ross's somewhat tongue-in-cheek DelegateClass.
Goodness.

Creating a whole new Counter class, OTOH, seems like an excellent
alternative. Since I'd indicated that the reason I wanted .increment in
the first place was to make it quite clear that the actual value of the
thing being incremented wasn't necessarily relevant, having an object
that does NOT like to be divided, .times{}'ed, or otherwise cooperate
with more normal 'integer' like behavior makes sense. (Yes, Dave's
Counter, as written, *would* do those things, but it doesn't have to be
so...)

Although I will admit to being rather surprised that the answer to the
question
	How do I do this?
		whatever = 1
		whatever.increment
		p whatever
		=> 2
is apparently "You Can't."

Interesting.
Kirk H. (Guest)
on 2006-06-03 02:23
(Received via mailing list)
On Friday 02 June 2006 4:02 pm, Dave H. wrote:

> Although I will admit to being rather surprised that the answer to the
> question
>  How do I do this?
>   whatever = 1
>   whatever.increment
>   p whatever
>   => 2
> is apparently "You Can't."

Why is it surprising?

And you almost can, sort of, by relying on the syntactic similarity
between a
method call and a variable reference and using a simple counter object.

self.whatever = 1
whatever.increment
p whatever
=> 2

That, you can do.

-----
class IncObj
        def initialize(val)
                @me = @val.to_i
        end

        def to_s
                @me.to_s
        end

        def to_i
                @me
        end

        def increment
                @me += 1
        end
end

def whatever; @whatever; end

def whatever=(val); @whatever = IncObj.new(val); end

self.whatever = 1
whatever.increment
p whatever
-----


Kirk H.
unknown (Guest)
on 2006-06-03 03:26
(Received via mailing list)
On Jun 2, 2006, at 6:02 PM, Dave H. wrote:
>
> Although I will admit to being rather surprised that the answer to
> the question
> 	How do I do this?
> 		whatever = 1
> 		whatever.increment
> 		p whatever
> 		=> 2
> is apparently "You Can't."

If you are surprised by this then you haven't fully absorbed Ruby's
object model and/or assignment semantics.

I know that assignments such as 'whatever = 1' are often described as
copying the value 1 into the variable 'whatever' but I find that
language highly misleading for Ruby.  Not everyone thinks about it
the way I do but read on if you are curious.

Variables in Ruby are not containers for objects (or for values).  If
they can be said to 'hold' anything, then they hold references to
objects (not the object itself). Similarly, integer literals do not
represent values or objects but instead are labels bound
(permanently) to particular references to instances of Fixnum (I'm
ignoring BigNums).

   4

is a constant reference to the Fixnum instance that behaves like the
number 4. There is exactly one Fixnum instance that behaves that way.
There are many ways to identify that object (4, 0b100, 0x4, 0x04) but
all those labels are references to the same Fixnum instance.

Assignment in Ruby is just the re-binding of a label from one object
reference to another object reference.

   a = b

causes 'a' to be bound to the same object reference that is
associated with 'b'.

   a = 5

causes 'a' to be associated with the same object reference that is
associated with 5, i.e. the reference (there can be only one)
to the Fixnum object that behaves like the integer 5.  If you want an
expression like:

   a.increment

to cause 'a' to be associated with the Fixnum instance 6 then you are
talking about rebinding the variable 'a' so that it no longer is
bound to the reference to the Fixnum 5. But the only way to rebind a
variable is via assignment, so you are back to something like

   a = a + 1
or
   a = a.succ


Internally Ruby doesn't actually allocate memory for instances of
Fixnum.  The internal representation of object references is
structured so that Ruby can fake the existence of Fixnum instances
(and other objects such as nil, true, and false).  Ruby manipulates
the references, the objects themselves are never explicitly realized.
This topic is locked and can not be replied to.