[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

unintuitive language feature (exclamation functions

Nick Brown

8/20/2008 7:45:00 PM

I was surprised to discover that the code

astring.sub!(/hi/, 'bye')

behaves subtly differently from

astring = astring.sub(/hi/, 'bye')

Intuitively, to me, these should be identical. Perhaps the documentation
should make mention of this difference? A note about this unexpected
behavior would have saved me a lot of frustration, and would likely do
the same for many others new to Ruby.

To be honest, I'm still trying to find out exactly why these do
different things. The difference does not manifest itself with trivial
cases in irb; rather it shows up when I'm getting a string from cgi,
modifying it, then inserting it into a database. When using sub!, the
database ends up containing the pre-sub'd value of astring, even though
astring appears to contain the modified version when printed with a
debug statement immediately preceding my database insert.

I'm willing to except the criticism that my intuition is perverse in
some way, but when I started writing in Ruby I was really hoping it
would be a language one could use without having to understand how the C
underneath it all worked (defeating part of the purpose of "high level"
languages).

So what do you think? Would warnings in the documentation on exclamation
functions be useful or pointless?
--
Posted via http://www.ruby-....

19 Answers

F. Senault

8/20/2008 8:00:00 PM

0

Le 20 août 2008 à 21:45, Nick Brown a écrit :

> When using sub!, the
> database ends up containing the pre-sub'd value of astring, even though
> astring appears to contain the modified version when printed with a
> debug statement immediately preceding my database insert.

Please provide some code to demonstrate this. I'm willing to bet
there's another, subtler, step that's misleading you.

Fred
--
This world rejects me This world threw me away This world never gave
me a chance This world's gonna have to pay I don't believe in your
institutions I did what you wanted me to Like cancer in the system
I've got a little suprise for you (Nine Inch Nails, Burn)

Gregory Brown

8/20/2008 8:02:00 PM

0

On Wed, Aug 20, 2008 at 3:45 PM, Nick Brown
<ruby-forum.com@nick-brown.com> wrote:
> I was surprised to discover that the code
>
> astring.sub!(/hi/, 'bye')
>
> behaves subtly differently from
>
> astring = astring.sub(/hi/, 'bye')
>
> Intuitively, to me, these should be identical. Perhaps the documentation
> should make mention of this difference? A note about this unexpected
> behavior would have saved me a lot of frustration, and would likely do
> the same for many others new to Ruby.

If the two were identical, why would we have both sub and sub! methods?
The extra punctuation would be useless if it existed 'just for fun'

> To be honest, I'm still trying to find out exactly why these do
> different things. The difference does not manifest itself with trivial
> cases in irb; rather it shows up when I'm getting a string from cgi,
> modifying it, then inserting it into a database. When using sub!, the
> database ends up containing the pre-sub'd value of astring, even though
> astring appears to contain the modified version when printed with a
> debug statement immediately preceding my database insert.

The documentation for String#sub! is:

"Performs the substitutions of String#sub in place, returning str, or
nil if no substitutions were performed. "

> I'm willing to except the criticism that my intuition is perverse in
> some way, but when I started writing in Ruby I was really hoping it
> would be a language one could use without having to understand how the C
> underneath it all worked (defeating part of the purpose of "high level"
> languages).

This has nothing to do with C. It has to do with interface design,
and is meant to make things more intuitive, not less.
Admittedly there is nothing inherently intuitive about some_method!,
except that it might make you feel like you should pay more attention,
like... Caution!

Once learned, this convention can be very helpful.

> So what do you think? Would warnings in the documentation on exclamation
> functions be useful or pointless?

Since exclamation points are conventional and not behaviorly enforced
in any way by Ruby itself, all ! methods should come with their own
documentation.
It does not necessarily mean 'modify the receiver in place', so
further explanation is usually needed. Just remember that when you
see foo and foo!, the latter is the one that the developer of the
library you are using has indicated to require more attention, or be
more specialized.

If you're still not convinced, I recommend checking out a post by
David Black on this topic, as it clearly explains the value of the
convention:
http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-wi...

-greg

--
Technical Blaag at: http://blog.majesticseacr... | Non-tech
stuff at: http://metametta.bl...

Stefano Crocco

8/20/2008 8:15:00 PM

0

On Wednesday 20 August 2008, Nick Brown wrote:
> I was surprised to discover that the code
>
> astring.sub!(/hi/, 'bye')
>
> behaves subtly differently from
>
> astring = astring.sub(/hi/, 'bye')
>
> Intuitively, to me, these should be identical. Perhaps the documentation
> should make mention of this difference? A note about this unexpected
> behavior would have saved me a lot of frustration, and would likely do
> the same for many others new to Ruby.
>
> To be honest, I'm still trying to find out exactly why these do
> different things. The difference does not manifest itself with trivial
> cases in irb; rather it shows up when I'm getting a string from cgi,
> modifying it, then inserting it into a database. When using sub!, the
> database ends up containing the pre-sub'd value of astring, even though
> astring appears to contain the modified version when printed with a
> debug statement immediately preceding my database insert.
>
> I'm willing to except the criticism that my intuition is perverse in
> some way, but when I started writing in Ruby I was really hoping it
> would be a language one could use without having to understand how the C
> underneath it all worked (defeating part of the purpose of "high level"
> languages).
>
> So what do you think? Would warnings in the documentation on exclamation
> functions be useful or pointless?

Unless I misunderstood you, you're asking why two different methods
(String#sub and String#sub!) work differently. The answer is simple: because
they're different. It's like asking why String#upcase and String#downcase work
differently.

The documentation do speak of this difference:

ri String#sub gives:

------------------------------------------------------------- String#sub
str.sub(pattern, replacement) => new_str
str.sub(pattern) {|match| block } => new_str
------------------------------------------------------------------------
Returns a copy of _str_ with the _first_ occurrence of _pattern_
replaced with either _replacement_ or the value of the block. [...]

while ri String#sub! gives:

------------------------------------------------------------ String#sub!
str.sub!(pattern, replacement) => str or nil
str.sub!(pattern) {|match| block } => str or nil
------------------------------------------------------------------------
Performs the substitutions of +String#sub+ in place, returning
_str_, or +nil+ if no substitutions were performed.

You don't need to know about the C implementation of class String, of
String#sub or of String#sub! to understand how these methods work. The
documentation says that sub returns a copy of the string with the replacement
done, which means a different object, which has nothing to do with the
original. In the case of sub!, instead, the substitution is done in place,
that is, the receiver itself (str) is modified, not a copy of it.

As for the fact that the difference doesn't show in irb, this is not true.
Look at this:

irb(main):001:0> str = "this is a test string"
=> "this is a test string"
irb(main):002:0> str1 = str.sub "h", "H"
=> "tHis is a test string"
irb(main):003:0> str
=> "this is a test string"

The above lines show that str is not changed by sub

irb(main):004:0> str.sub "k", "K"
=> "this is a test string"
irb(main):005:0> str.sub! "k", "K"
=> nil

This shows the different behavior concerning the return value when there's
nothing to replace. sub returns a copy of the string without modifications,
while sub! returns nil

irb(main):006:0> str.sub! "a", "A"
=> "this is A test string"
irb(main):007:0> str
=> "this is A test string"
irb(main):008:0>

Here you can see that sub!, unlike sub, changes the original string.

In short, here's the difference between sub and sub!:
* sub creates a new string which has the same contents of the original one,
but is indipendent from, then replaces the pattern with the replacement text
in the copy. The original is not altered in any way. It always returns the
copy and you can see whether a replacement has been made by comparing the
original and the copy.
* sub! performs the replacement on the string itself, thus changing it.
Obviously, you can't compare the 'new' and the 'original' string to see
whether a replacement has been made (since there's no 'new string' and the
original has been changed), so you have to look at the return value: if it is
nil, nothing has been changed; if it is the string itself then a replacement
has been made.

I hope this helps

Stefano

Nick Brown

8/20/2008 8:25:00 PM

0

F. Senault wrote:
> Please provide some code to demonstrate this.

#!/usr/bin/env ruby

require 'sqlite3'
db = SQLite3::Database.new('test.sqlite')
db.execute ('drop table if exists example') # clean up incase of
multiple runs
db.execute('create table example (aval)')

require 'cgi'
cgi = CGI.new('html4')

a = cgi['a']

a.sub!(/hi/, 'bye')
# to see expected behavior, replace the above with: a = a.sub(/hi/,
'bye')

puts "Inserting value a=#{a} into the database.\n"
sql = "insert into example (aval) values (?)"
db.execute(sql, a)

sql = "select aval from example"
val = db.get_first_value(sql)
puts "What was actually inserted into the database: #{val}\n"


########---------- end of code

To run this, type "a=hi"[enter][ctrl-d] to simulate the behavior of a
cgi session. You will get the output:

Inserting value a=bye into the database.
What was actually inserted into the database: hi

Since other responders seem to think I expect sub to behave the same as
sub!, I don't. I expect str.sub! to modify str, and I expect str.sub to
return a modified copy of the str. This is not the same behavior.
--
Posted via http://www.ruby-....

Stefano Crocco

8/20/2008 8:52:00 PM

0

On Wednesday 20 August 2008, Nick Brown wrote:
> F. Senault wrote:
> > Please provide some code to demonstrate this.
>
> #!/usr/bin/env ruby
>
> require 'sqlite3'
> db = SQLite3::Database.new('test.sqlite')
> db.execute ('drop table if exists example') # clean up incase of
> multiple runs
> db.execute('create table example (aval)')
>
> require 'cgi'
> cgi = CGI.new('html4')
>
> a = cgi['a']
>
> a.sub!(/hi/, 'bye')
> # to see expected behavior, replace the above with: a = a.sub(/hi/,
> 'bye')
>
> puts "Inserting value a=#{a} into the database.\n"
> sql = "insert into example (aval) values (?)"
> db.execute(sql, a)
>
> sql = "select aval from example"
> val = db.get_first_value(sql)
> puts "What was actually inserted into the database: #{val}\n"
>
>
> ########---------- end of code
>
> To run this, type "a=hi"[enter][ctrl-d] to simulate the behavior of a
> cgi session. You will get the output:
>
> Inserting value a=bye into the database.
> What was actually inserted into the database: hi
>
> Since other responders seem to think I expect sub to behave the same as
> sub!, I don't. I expect str.sub! to modify str, and I expect str.sub to
> return a modified copy of the str. This is not the same behavior.

If in your first post you'd have stated more clearly what you expected and
what you instead got, we wouldn't have misunderstood your needs. After all,
the only (or, at least, main) difference between sub and sub! is the one I
spoke of in my other answer. However, I can't try your code, as I don't have
the sqlite gem/library. Would you please post what you get using sub and what
you get using sub!?

The line

puts "Inserting value a=#{a} into the database.\n"

displays the correct value (a=bye). If I understand you correctly, the
surprising behavior comes from inserting it in the database. Posting what you
get from the other puts will enable also those who don't have sqlite to help
you.

(By the way, you don't need to put the \n at the end of the string with puts).

Stefano

Patrick Li

8/20/2008 8:58:00 PM

0

I would agree with Stefano. I doesn't look like an issue with sub and
sub! to me.
I ran into something similar with my webapp. For me, it was because I
didn't call database.commit() after my update statement.
--
Posted via http://www.ruby-....

F. Senault

8/20/2008 8:59:00 PM

0

Le 20 août 2008 à 22:25, Nick Brown a écrit :

> F. Senault wrote:
>> Please provide some code to demonstrate this.

Don't ask me why (yet) but...

> a = cgi['a']
a = cgi['a'].dup

and...

22:47 fred@balvenie:~/> ruby test.rb
(offline mode: enter name=value pairs on standard input)
a=hi
Inserting value a=bye into the database.
What was actually inserted into the database: bye

....

It seems that CGI does horrible, horrible things to its strings :

require 'cgi'
cgi = CGI.new('html4')

a = cgi['a'] #.dup
b = cgi['a'].dup

puts "A :"
a.sub!(/hi/, 'bye')
puts a.to_s
puts a.inspect
puts a.class

puts "B :"
b.sub!(/hi/, 'bye')
puts b.to_s
puts b.inspect
puts b.class

Gives :

22:53 fred@balvenie:~> ruby test.rb
(offline mode: enter name=value pairs on standard input)
a=hi
A :
hi
"bye"
String
B :
bye
"bye"
String

Ugh !

Fred
--
Tried to save a place from the cuts and the scratches
Tried to overcome the complications and the catches
Nothing ever grows and the sun doesn't shine all day (Nine Inch Nails,
Tried to save myself but myself keeps slipping away Into the Void)

brabuhr

8/20/2008 9:00:00 PM

0

On Wed, Aug 20, 2008 at 4:25 PM, Nick Brown
<ruby-forum.com@nick-brown.com> wrote:
> F. Senault wrote:
>> Please provide some code to demonstrate this.
>
> a = cgi['a']

Internal to the CGI object, it appears that "a" in the @params hash is
an array of strings not a string:

irb(main):007:0> cgi = CGI.new('html4')
(offline mode: enter name=value pairs on standard input)
a=foohibyebar
=> #<CGI:0xb7c998cc @params={"a"=>["foohibyebar"]}, @multipart=false,
@output_cookies=nil, @output_hidden=nil, @cookies={}>

> a.sub!(/hi/, 'bye')
> # to see expected behavior, replace the above with: a = a.sub(/hi/,
> 'bye')
>
> To run this, type "a=hi"[enter][ctrl-d] to simulate the behavior of a
> cgi session. You will get the output:
>
> Inserting value a=bye into the database.
> What was actually inserted into the database: hi

> cat z.rb
#!/usr/bin/env ruby

require 'rubygems'
require 'sqlite3'

db = SQLite3::Database.new('test.sqlite')
db.execute ('drop table if exists example') # clean up incase of multiple runs
db.execute('create table example (aval)')

require 'cgi'
cgi = CGI.new('html4')

a = cgi['a'][0]

a.sub!(/hi/, 'bye')
# to see expected behavior, replace the above with: a = a.sub(/hi/, 'bye')

puts "Inserting value a=#{a} into the database.\n"
sql = "insert into example (aval) values (?)"
db.execute(sql, a)

sql = "select aval from example"
val = db.get_first_value(sql)
puts "What was actually inserted into the database: #{val}\n"

> ruby z.rb
z.rb:7: warning: don't put space before argument parentheses
(offline mode: enter name=value pairs on standard input)
a=foohibyebar
z.rb:13:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array,
use cgi.params['key']
Inserting value a=foobyebyebar into the database.
What was actually inserted into the database: foobyebyebar

Nick Brown

8/20/2008 9:01:00 PM

0

I'm starting to wonder if this is actually a bug in Ruby? The
documentation of sub! says it should modify the string in place.

The code I posted does something different. After the a.sub!, executing
"puts #{a}" outputs the modified version of a, but inserting that *exact
same* string object into a database puts an UNMODIFIED version of the
string into the DB. It's as if db.execute looks back in time to before
the sub! when it gets the value of a. Something unexplained is going on
here (unless the database module includes a time machine).

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

brabuhr

8/20/2008 9:02:00 PM

0

On Wed, Aug 20, 2008 at 5:03 PM, <brabuhr@gmail.com> wrote:
> On Wed, Aug 20, 2008 at 4:25 PM, Nick Brown
> <ruby-forum.com@nick-brown.com> wrote:
>> F. Senault wrote:
>>> Please provide some code to demonstrate this.
>>
>> a = cgi['a']
>
> Internal to the CGI object, it appears that "a" in the @params hash is
> an array of strings not a string:
>
> irb(main):007:0> cgi = CGI.new('html4')
> (offline mode: enter name=value pairs on standard input)
> a=foohibyebar
> => #<CGI:0xb7c998cc @params={"a"=>["foohibyebar"]}, @multipart=false,
> @output_cookies=nil, @output_hidden=nil, @cookies={}>

> ruby z.rb
z.rb:7: warning: don't put space before argument parentheses
(offline mode: enter name=value pairs on standard input)
a=foohibybar
a=zizzlesticks
z.rb:13:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array,
use cgi.params['key']
Inserting value a=foobyebybar into the database.
What was actually inserted into the database: foobyebybar