In-place parameter modification

Native to ruby are several methods that change passed-in parameters
in-place, such as chomp!(somestring). But I don't see any info on
creating these things myself. Is it possible to write methods that
change parameters this way?

···

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

Actually, chomp!(string) doesn't modify string, it modifies $_ (which, if I
recall correctly, is set by the -n and -p switches to ruby).

String#chomp! modifies the recipient.

Finally: ruby is a pass-by-reference language, so if you actually do make
changes the parameters of a method, the callers variables will change as
well. For instance:

def make_bacon(string)
  string.replace "Chunky bacon."
end

hi = "hello"
make_bacon(hi)
hi #=> "Chunky bacon."

Judson

···

On Fri, Oct 30, 2009 at 4:38 PM, Dave Anderson <anderson@crocker.com> wrote:

Native to ruby are several methods that change passed-in parameters
in-place, such as chomp!(somestring). But I don't see any info on
creating these things myself. Is it possible to write methods that
change parameters this way?
--
Posted via http://www.ruby-forum.com/\.

Hello,

Dave Anderson wrote:

Native to ruby are several methods that change passed-in parameters
in-place, such as chomp!(somestring). But I don't see any info on
creating these things myself. Is it possible to write methods that
change parameters this way?

Yup, you can write your own mutating methods that mutate the
receiver(like chomp!, gsub!, sub!, and so on), but you can't mutate any
of the core classes because you cannot reassign self.

If it were your own class, though, you might have:

class MyString

  def initialize str
    @str = str
  end

  def append!
    @str << ' ruby'
  end

  def to_s
    @str
  end

end

str = MyString.new 'I love '
str.append!
puts str # => 'I love ruby'

chomp! takes one argument, and that is a seperator(or what to remove
from the end of the string)

Hope this helps some,
Rob

···

--
Posted via http://www.ruby-forum.com/\.

Judson Lester wrote:

Finally: ruby is a pass-by-reference language, so if you actually do
make
changes the parameters of a method, the callers variables will change as
well.

def test(x)
  x = 10
end

num = 5
test num
puts num

--output:--
5

???

···

--
Posted via http://www.ruby-forum.com/\.

Some core classes know how to replace themselves:

class String
   def clobber!
     replace ''
   end
end

I wonder why Object (or BasicObject) doesn't know this trick.

I also wonder why "replace" isn't called "replace!" :slight_smile:

···

On Oct 31, 2009, at 12:44 PM, Robert Gleeson wrote:

Yup, you can write your own mutating methods that mutate the
receiver(like chomp!, gsub!, sub!, and so on), but you can't mutate any
of the core classes because you cannot reassign self.

--
  Jordi

7stud -- wrote:

Judson Lester wrote:

Finally: ruby is a pass-by-reference language, so if you actually do
make
changes the parameters of a method, the callers variables will change as
well.

def test(x)
  x = 10
end

num = 5
test num
puts num

--output:--
5

???

My educated guess is that string.replace("Chunky bacon") is operating on
the original, whereas string = "Chunky bacon" just makes a new local
copy. The pickaxe book suggests that this only works on Arrays, Hashes
and Strings, though, so there's no way to make replace work on a fixnum.

···

--
Posted via http://www.ruby-forum.com/\.

Jordi Bunster wrote:

···

On Oct 31, 2009, at 12:44 PM, Robert Gleeson wrote:

Yup, you can write your own mutating methods that mutate the
receiver(like chomp!, gsub!, sub!, and so on), but you can't mutate
any
of the core classes because you cannot reassign self.

Some core classes know how to replace themselves:

class String
   def clobber!
     replace ''
   end
end

I wonder why Object (or BasicObject) doesn't know this trick.

I also wonder why "replace" isn't called "replace!" :slight_smile:

This is cool, but it only works for mutating methods that already exist,
if you want to write your own you're out of luck, as far as the core
classes go.
--
Posted via http://www.ruby-forum.com/\.

Hi --

Yup, you can write your own mutating methods that mutate the
receiver(like chomp!, gsub!, sub!, and so on), but you can't mutate any
of the core classes because you cannot reassign self.

Some core classes know how to replace themselves:

class String
def clobber!
  replace ''
end

I wonder why Object (or BasicObject) doesn't know this trick.

I don't think "replace" has any generalizable semantics. You can't
replace 2 with 3, for example (fortunately :slight_smile:

I also wonder why "replace" isn't called "replace!" :slight_smile:

The !/non-! methods come in pairs. The ! one is the "dangerous" one --
that is, the one that probably has side-effects, special-case
semantics, etc. A lot of the "danger" involves changing the receiver,
but not all of it (e.g., exit!).

Therefore, if it's just one method, not a pair, it simply has a name
that says what it does, without a !. String#replace has to be
destructive. Otherwise the name is absurd. (str.replace("abc") can be
better written as just "abc", if all it's doing is creating a
completely new string with the characters "abc".)

There's no one-to-one correspondence, in either direction, between
receiver-changing methods and the !. Array#pop and friends, the
various replace and clear methods, Hash#update -- there are lots of
them.

Here's some further reading on the !-convention (and its lamentable
near-disappearance):

http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist

David

···

On Sun, 1 Nov 2009, Jordi Bunster wrote:

On Oct 31, 2009, at 12:44 PM, Robert Gleeson wrote:

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

As long as you've been hanging out here, I'm surprised if you are
really surprised by this.

In order to get completely comfortable with Ruby you need to
understand the relationships and differences between objects and
variables

Objects are instances of classes, and typically have state, in some
cases like Fixnums the only state is really the object's identity, and
such objects are immutable, if two such objects have different state,
then they MUST be different objects. There is only one 1 object and
one 5 object. Symbols are similar in that there is only one instance
of symbol with a particular value.

Some clases have methods which change the state of an object without
changing it's identity. String.gsub! is one such method. You can
write such mutating methods yourself, clearly for your own classes,
but also for core classes which already have mutating methods by
writing new methods interms of the existing mutating methods.

A variable is not an object, rather it is a temporary binding to a
particular object which is established by, and can be changed by an
assignment expression, syntactic sugar which disguises an assignment
expression (e.g. x += y). Variables include locals, method
parameters, instance variables, element slots in arrays, and block
arguments (which are distinguished from local variables in Ruby 1.9).

So variables can't be mutated, they can only have their binding
switched to another variable, if two variables reference the same
object, and the binding of one of those variables changes to reference
another object, the other variable is unaffected.

def test(x)
x = 10
end

num = 5
test num
puts num

--output:--
5
But, again since variables are bindings to objects, if two variables
reference the same object, and that object is mutated, then the change
in state will by visible through ANY reference to that particular
object.

x and num are two different variables. At the time test is called,
num is bound to 5, inside the context of the invocation of test, x
starts out bound to 5, then the assignment changes the binding of x
to 1, and leaves the binding of num alone. Imagine the code had been:

num=5
x = 1
puts num
output:
5

I hope that this is completely unsurprising, I suspect the surprise
might come from the assumption that somehow x and num are the same
variable because of the formal/actual parameter pairing.

In the case of method arguments (and block arguments in Ruby 1.9) the
name is local to the method (or block). This means that x and num are
indeed not the same variable, but two variables one of which (x) is
initialized by the call to refer to the same object resulting from the
expression used in the call, which in this cases happen to simply be
the variable num.

Imagine that we'd called

test(num + 1)

Here the actual value would be not 5 but 6, and there's no visible
variable which corresponds to the actual expression.

And even if the FORMAL parameter name were changed:

def test(num)
num = 10
end

num = 5
test num
puts num

--output:--
5

The result would be the same, since the names num at the top level,
and num inside the test method are two different variables.

HTH

And of course there's my oldie but goodie:

http://talklikeaduck.denhaven2.com/2006/09/13/on-variables-values-and-objects

···

On Sat, Oct 31, 2009 at 5:03 AM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:

Judson Lester wrote:

Finally: ruby is a pass-by-reference language, so if you actually do
make
changes the parameters of a method, the callers variables will change as
well.

def test(x)
x = 10
end

num = 5
test num
puts num

--output:--
5

???

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

Hi --

···

On Sun, 1 Nov 2009, Dave Anderson wrote:

7stud -- wrote:

Judson Lester wrote:

Finally: ruby is a pass-by-reference language, so if you actually do
make
changes the parameters of a method, the callers variables will change as
well.

def test(x)
  x = 10
end

num = 5
test num
puts num

--output:--
5

???

My educated guess is that string.replace("Chunky bacon") is operating on
the original, whereas string = "Chunky bacon" just makes a new local
copy. The pickaxe book suggests that this only works on Arrays, Hashes
and Strings, though, so there's no way to make replace work on a fixnum.

That's a good thing :slight_smile:

David

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

I wonder why Object (or BasicObject) doesn't know this trick.

I also wonder why "replace" isn't called "replace!" :slight_smile:

This is cool, but it only works for mutating methods that already exist,
if you want to write your own you're out of luck, as far as the core
classes go.

Unless you hack in C or use evil.rb.
They're just C structs, ultimately, and you can do ALL sorts of fun with them.

Immediates could be tricky, though, since the reference to them /is/ the data. No struct in sight.

Rick Denatale wrote:
...

Objects are instances of classes, and typically have state, in some
cases like Fixnums the only state is really the object's identity, and
such objects are immutable, if two such objects have different state,
then they MUST be different objects. There is only one 1 object and
one 5 object. Symbols are similar in that there is only one instance
of symbol with a particular value.

Thanks for the fascinating and valuable insights, Rick. I didn't realize
what a can of worms I was opening here. And it's nice to know that
someone here remembers Fortran II, although that might make you a good
candidate for a Men-In-Black neurolizer. :wink: As an old hand at C++, this
variable/value/type stuff is a very worthwhile lesson.

One practical thing I can draw out of this is the difference between
these two functions:

  def X_1( str )
    str[0] = 'X'
  end

  def X_2( str )
     str = 'X' + str[ 1..-1 ]
  end

The X_1 function alters the passed-in object in place, while the X_2
function modifies a local copy. I don't recall reading this in the
literature anywhere.

···

--
Posted via http://www.ruby-forum.com/\.

I don't think "replace" has any generalizable semantics. You can't
replace 2 with 3, for example (fortunately :slight_smile:

You could just memcpy a to b (as long as their classes are eql?). If for a given object it doesn't make sense, like for singleton ones, then you don't allow it.

I wonder if they would take a patch. Is not as if Ruby doesn't already have a ton of edge cases anyway.

I also wonder why "replace" isn't called "replace!" :slight_smile:

The !/non-! methods come in pairs. The ! one is the "dangerous" one --
that is, the one that probably has side-effects, special-case
semantics, ...

Ok, that makes sense now.

···

On Oct 31, 2009, at 1:03 PM, David A. Black wrote:

--
  Jordi

Dave Anderson wrote:

Rick Denatale wrote:
...

Objects are instances of classes, and typically have state, in some
cases like Fixnums the only state is really the object's identity, and
such objects are immutable, if two such objects have different state,
then they MUST be different objects. There is only one 1 object and
one 5 object. Symbols are similar in that there is only one instance
of symbol with a particular value.

Thanks for the fascinating and valuable insights, Rick. I didn't realize
what a can of worms I was opening here. And it's nice to know that
someone here remembers Fortran II, although that might make you a good
candidate for a Men-In-Black neurolizer. :wink: As an old hand at C++, this
variable/value/type stuff is a very worthwhile lesson.

One practical thing I can draw out of this is the difference between
these two functions:

  def X_1( str )
    str[0] = 'X'
  end

  def X_2( str )
     str = 'X' + str[ 1..-1 ]
  end

The X_1 function alters the passed-in object in place,

Remember that in ruby "=" is the name of a method:

str[0] = 'X'

And a method can be programmed to alter the original string and return
it; or it can create a new string that is a copy of the original and
return the altered copy.

I don't recall reading this in the literature anywhere.

$ri String#=
------------------------------------------------------------- String#=
     str[fixnum] = fixnum
     str[fixnum] = new_str
     str[fixnum, fixnum] = new_str
     str[range] = aString
     str[regexp] = new_str
     str[regexp, fixnum] = new_str
     str[other_str] = new_str

···

------------------------------------------------------------------------
Element Assignment---***Replaces some or all of the content of _str_
(that should read 'str' in my opinion).***

while the X_2
function modifies a local copy.

With a basic assignment statement like this:

str = 'X' + str[ 1..-1 ]

'=' is not the name of a String method. Instead:

---
This form of assignment is hardwired into the language.
---
(pickaxe2, p. 90)

And that type of assignment in ruby works like this:

If you write this:

x = 10

you get this:

x --> 10

In other words, x refers to 10. If you then write:

y = 10

you get:

x-----> 10
        ^
        >
y ------+

Then if you write:

y = 20

you do not get:

x-----> 20
        ^
        >
y ------+

Instead you get:

x ----> 10

y ----> 20

I like to think of it like this: with that basic type of assignment you
take the variable name on the left of the assignment, 'y' in this case,
and write it on a stick-it note, and paste the stick-it note on the
object on the right, the object 20. If ruby has already written the
same variable name on a stick-it note and pasted it on some other object
prior to your assignment statement(in the same scope), then ruby tears
the stick-it note off the other object and pastes it on the object 20.

--
Posted via http://www.ruby-forum.com/\.

Hi --

I don't think "replace" has any generalizable semantics. You can't
replace 2 with 3, for example (fortunately :slight_smile:

You could just memcpy a to b (as long as their classes are eql?). If for a given object it doesn't make sense, like for singleton ones, then you don't allow it.

I wonder if they would take a patch. Is not as if Ruby doesn't already have a ton of edge cases anyway.

The "There's a precedent" argument can always be inverted, though,
into the "We've reached our quota" argument :slight_smile: Of course, that's
where Matz and his wonderful sense of balance come in.

In any case, I wouldn't call such a method "replace", because it's
radically different from the existing replace methods. Somewhere
there's at least one implementation (maybe in evil.rb) of a method
called "become", which I think does what you're describing, or
something similar: a.become(b) means that all existing references to a
become references to b, if I'm remembering/summarizing correctly.

David

···

On Sun, 1 Nov 2009, Jordi Bunster wrote:

On Oct 31, 2009, at 1:03 PM, David A. Black wrote:

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

Actually, the X_2 function doesn't make a local copy of the string
passed as the argument.

It ends up creating three new strings.

1 A copy of the literal string 'X'
2 A string which is the result of str[1..-1]
3 A string which is the concatenation of strings 1 and 2 which is then
bound to the local variable str. and will be the result of the method
call since it's the last expression and the value of an assignment
expression is the right hand side of the assignment.

···

On Sat, Oct 31, 2009 at 5:50 PM, Dave Anderson <anderson@crocker.com> wrote:

One practical thing I can draw out of this is the difference between
these two functions:

def X_1( str )
str[0] = 'X'
end

def X_2( str )
str = 'X' + str[ 1..-1 ]
end

The X_1 function alters the passed-in object in place, while the X_2
function modifies a local copy. I don't recall reading this in the
literature anywhere.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

7stud -- wrote:

Because you mentioned C++, I'll just point out that "ruby references"
are not like C++ reference types. In C++, a reference variable is an
alias for another variable, which means that both variables refer to the
same location in memory. If you use one of the variable names to change
what is at that location in memory, the other variable, because it
retrieves its value from the same location in memory, also refers to the
changed value.

In my ruby example above, x and y are not aliases--even though they
refer to the same object 10. As a result, when y is assigned a new
value, it does not affect x.

···

--
Posted via http://www.ruby-forum.com/\.