String > Integer Conversion Problem

M

Matthew Feadler

Retro thanks to all who helped me with my last post. I'm certainly more
comfortable with Ruby now than then, but still a newbie as the following
will surely demonstrate.

Below, you can see that I'm checking command-line arguments to the
program for various conditions. Unfortunately, because the args are
stored as strings, when I convert them via to_i, empty and non-numerical
strings become 0. 0 is an acceptable element in this program, therefore
I can't use it to test for invalid input. I've worked around this (sort
of), by creating two sets of variables for the arguments (one set as
strings, one set as integers). Unfortunately, this complicates the
code, and more importantly, leaves me stumped concerning how to test for
non-numeric values.

So, the program does what I want, except when the args are non-numeric
strings, and the code seems uglier than it ought to be.

Thanks in advance,

-ELf

def gen_chart(max)
x=0
y=0
local_chart = Array.new
while x<=max
y+=x
local_chart[x]=y
x+=1
end
local_chart
end

arg0 = ARGV[0]
arg1 = ARGV[1]

arg0i = ARGV[0].to_i
arg1i = ARGV[1].to_i

if arg0.nil? or (arg0i < 0) or (arg1i < 0)
#print usage
print <<-EOS

No args, or bad args!

Usage: #$0 [maxvalue]
#$0 [minvalue] [maxvalue]
EOS
elsif arg1.nil?
#do chart to arg0, print last pair
chart = gen_chart(arg0i)
puts arg0i.to_s + ": " + chart[arg0i].to_s
else
#do chart to arg1, print pairs from arg0 to last
chart = gen_chart(arg1i)
x=arg0i
y=arg1i
while x<=y
puts x.to_s + ": " + chart[x].to_s
x+=1
end
end
 
J

James Edward Gray II

Below, you can see that I'm checking command-line arguments to the
program for various conditions.

See if this gives you some ideas:

if ARGV.all? { |n| n =~ /\A\d+\Z/ }
puts "Usage..."
exit
else
one, two = ARGV.map { |n| Integer(n) }
end

Hope that helps.

James Edward Gray II
 
J

Joe Van Dyk

Retro thanks to all who helped me with my last post. I'm certainly more
comfortable with Ruby now than then, but still a newbie as the following
will surely demonstrate.

Below, you can see that I'm checking command-line arguments to the
program for various conditions. Unfortunately, because the args are
stored as strings, when I convert them via to_i, empty and non-numerical
strings become 0. 0 is an acceptable element in this program, therefore
I can't use it to test for invalid input. I've worked around this (sort
of), by creating two sets of variables for the arguments (one set as
strings, one set as integers). Unfortunately, this complicates the
code, and more importantly, leaves me stumped concerning how to test for
non-numeric values.

So, the program does what I want, except when the args are non-numeric
strings, and the code seems uglier than it ought to be.

Thanks in advance,

-ELf

def gen_chart(max)
x=3D0
y=3D0
local_chart =3D Array.new
while x<=3Dmax
y+=3Dx
local_chart[x]=3Dy
x+=3D1
end
local_chart
end

arg0 =3D ARGV[0]
arg1 =3D ARGV[1]

arg0i =3D ARGV[0].to_i
arg1i =3D ARGV[1].to_i

if arg0.nil? or (arg0i < 0) or (arg1i < 0)
#print usage
print <<-EOS

No args, or bad args!

Usage: #$0 [maxvalue]
#$0 [minvalue] [maxvalue]
EOS
elsif arg1.nil?
#do chart to arg0, print last pair
chart =3D gen_chart(arg0i)
puts arg0i.to_s + ": " + chart[arg0i].to_s
else
#do chart to arg1, print pairs from arg0 to last
chart =3D gen_chart(arg1i)
x=3Darg0i
y=3Darg1i
while x<=3Dy
puts x.to_s + ": " + chart[x].to_s
x+=3D1
end
end

Maybe this will give you an idea or two:

require 'test/unit'

def process_command_line_arguments args
results =3D []
args.each do |arg|
raise "Hey, '#{ arg }' needs to be a number" if !arg.match(/^\d*$/)
arg =3D arg.to_i
results << arg
end
results
end

class TestThis < Test::Unit::TestCase
def test_command_line_processing
assert_equal [2], process_command_line_arguments(["2"])
assert_equal [2, 3], process_command_line_arguments(["2", "3"])
assert_raise(RuntimeError) { process_command_line_arguments(["-1"]) }
assert_raise(RuntimeError) { process_command_line_arguments(["1", "oog"=
]) }
assert_raise(RuntimeError) { process_command_line_arguments(["boo", "oo=
g"])}
end
end



$ ruby a.rb
Loaded suite a
Started
 
J

Joe Van Dyk

Retro thanks to all who helped me with my last post. I'm certainly mor= e
comfortable with Ruby now than then, but still a newbie as the followin= g
will surely demonstrate.

Below, you can see that I'm checking command-line arguments to the
program for various conditions. Unfortunately, because the args are
stored as strings, when I convert them via to_i, empty and non-numerica= l
strings become 0. 0 is an acceptable element in this program, therefor= e
I can't use it to test for invalid input. I've worked around this (sor= t
of), by creating two sets of variables for the arguments (one set as
strings, one set as integers). Unfortunately, this complicates the
code, and more importantly, leaves me stumped concerning how to test fo= r
non-numeric values.

So, the program does what I want, except when the args are non-numeric
strings, and the code seems uglier than it ought to be.

Thanks in advance,

-ELf

def gen_chart(max)
x=3D0
y=3D0
local_chart =3D Array.new
while x<=3Dmax
y+=3Dx
local_chart[x]=3Dy
x+=3D1
end
local_chart
end

arg0 =3D ARGV[0]
arg1 =3D ARGV[1]

arg0i =3D ARGV[0].to_i
arg1i =3D ARGV[1].to_i

if arg0.nil? or (arg0i < 0) or (arg1i < 0)
#print usage
print <<-EOS

No args, or bad args!

Usage: #$0 [maxvalue]
#$0 [minvalue] [maxvalue]
EOS
elsif arg1.nil?
#do chart to arg0, print last pair
chart =3D gen_chart(arg0i)
puts arg0i.to_s + ": " + chart[arg0i].to_s
else
#do chart to arg1, print pairs from arg0 to last
chart =3D gen_chart(arg1i)
x=3Darg0i
y=3Darg1i
while x<=3Dy
puts x.to_s + ": " + chart[x].to_s
x+=3D1
end
end

Maybe this will give you an idea or two:

require 'test/unit'

def process_command_line_arguments args
results =3D []
args.each do |arg|
raise "Hey, '#{ arg }' needs to be a number" if !arg.match(/^\d*$/)
arg =3D arg.to_i
results << arg
end
results
end

class TestThis < Test::Unit::TestCase
def test_command_line_processing
assert_equal [2], process_command_line_arguments(["2"])
assert_equal [2, 3], process_command_line_arguments(["2", "3"])
assert_raise(RuntimeError) { process_command_line_arguments(["-1"]) }
assert_raise(RuntimeError) { process_command_line_arguments(["1", "oo= g"]) }
assert_raise(RuntimeError) { process_command_line_arguments(["boo", "= oog"])}
end
end



$ ruby a.rb
Loaded suite a
Started
.
Finished in 0.001163 seconds.

1 tests, 5 assertions, 0 failures, 0 errors

Whoops, this is probably better:

def process_command_line_arguments args
args.map do |arg|
raise "Hey, '#{ arg }' needs to be a number" if !arg.match(/^\d*$/)
arg.to_i
end
end

Passes all the tests.
 
J

Joe Van Dyk

See if this gives you some ideas:

if ARGV.all? { |n| n =3D~ /\A\d+\Z/ }
puts "Usage..."
exit
else
one, two =3D ARGV.map { |n| Integer(n) }
end

Neat, didn't know about Enumerable#all? or about Integer().
 
J

Jacob Fugal

The Integer() method raises ArgumentError if given an empty,
non-numeric, or otherwise non-well-formed string.

if ARGV.all? { |n| n =3D~ /\A\d+\Z/ }
puts "Usage..."
exit
else
one, two =3D ARGV.map { |n| Integer(n) }
end

So could we rewrite this as:

begin
one, two =3D ARGV.map{ |n| Integer(n) }
rescue ArgumentError
puts Usage
exit
end

?

Jacob Fugal
 
D

dblack

Hi --

Retro thanks to all who helped me with my last post. I'm certainly more
comfortable with Ruby now than then, but still a newbie as the following
will surely demonstrate.

Below, you can see that I'm checking command-line arguments to the
program for various conditions. Unfortunately, because the args are
stored as strings, when I convert them via to_i, empty and non-numerical
strings become 0. 0 is an acceptable element in this program, therefore
I can't use it to test for invalid input. I've worked around this (sort
of), by creating two sets of variables for the arguments (one set as
strings, one set as integers). Unfortunately, this complicates the
code, and more importantly, leaves me stumped concerning how to test for
non-numeric values.

So, the program does what I want, except when the args are non-numeric
strings, and the code seems uglier than it ought to be.

In addition to the other suggestions, you might find scanf useful:

require 'scanf'
arg0, arg1 = ARGV.join.scanf("%d%d")

or something like that.


David

--
David A. Black
(e-mail address removed)

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
 
J

Jacob Fugal

Jacob said:
begin
one, two =3D ARGV.map{ |n| Integer(n) }
rescue ArgumentError
puts Usage
exit
end

When the arguments to Integer are strings from the command line, yes. In
the general case Integer() can raise TypeError as well as ArgumentError,
for arguments like Integer([1,2])

True, but I *was* referring specifically to the command line; vis the
OP's question, and evidenced by my use of ARGV. :)

Jacob Fugal
 
J

James Edward Gray II

So could we rewrite this as:

begin
one, two = ARGV.map{ |n| Integer(n) }
rescue ArgumentError
puts Usage
exit
end

Yeah, that's better. Neither of our versions checks the number of
arguments though and we probably should...

James Edward Gray II
 
J

J. Ryan Sobol

--Apple-Mail-12-152094684
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed


Excuse my ruby "newb-ness", but what does this line actually do?
Mainly, what's throwing me off is the "one, two" assignment (or
whatever it is).
--Apple-Mail-12-152094684--
 
K

Kevin Brown

Excuse my ruby "newb-ness", but what does this line actually do?
Mainly, what's throwing me off is the "one, two" assignment (or
whatever it is).

Try:

irb(main):001:0> one, two = 3, 4
=> [3, 4]
irb(main):002:0> one
=> 3
irb(main):003:0> two
=> 4

It's a multiple assignment. It takes multiple values (or an array) and spits
them into what's on the left.

Map simply iterates through an array and runs the block (the thing in braces)
each item, storing the result.

Thus, we're converting everything in the ARGV array to an integer and stuffing
it into two variables. The reason everyone else was saying it would be a
good idea to check the length is the following:

irb(main):004:0> one, two = 3, 4, 5
=> [3, 4, 5]
irb(main):005:0> one
=> 3
irb(main):006:0> two
=> 4

Kinda bad to have mysteriously disappearing command line arguments. :) Play
around with it and I'm sure you'll get the hang of it. Oh, and welcome to
Ruby! :-D
 
D

Dan Hinz

--=-jrIMYx9zoe+hU6tPciQ8
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

I see really two things happening here, string->int conversions and
command line parsing. Once you have the conversions understood (a good
thing to know) consider exploring more stuff in the packages.

Let me point you to OptionParser and GetOptLong. They both provide some
very useful command line parsing capabilities. It takes a little
experimentation to really understand what's happening but well worth it
in my opinion.

-dwh-



--=-jrIMYx9zoe+hU6tPciQ8--
 
J

jwesley

I disagree that this particular use of an exception would be a no-no in
Java. Inability to parse input is justifiably an "exceptional" case.
And the handling of that exceptional case is also appropriate.

The big "no-no" about exception usage is true for any language: "Don't
use exceptions for flow control".

The following code is "wrong" for various reasons as well as violating
the "axiom" above:

begin
# display 1 through 10
i = 0
while true
unless i > 10
puts i
else
raise "End o' the line"
end
i += 1
end
rescue
end

Ruby provides enough mechanisms for "controlling the flow" that using
exceptions for "normal" conditions is definitely poor style, if not
worse.

BTW "continuations" (related to exceptions) are a fairly powerful tool
to handle the times when strange flow control might be needed.
 
D

dblack

Hi --

Agreed, I was referring more to the idea of swapping out flow control for
exceptions in general. Obviously if the input is an exceptional case, throw
an exception. I have just been wondering about a few examples of this I've
seen in Ruby code, and just picked this as an 'in' to ask about it ;)


Okay, good. That was my feeling too.

I agree in general, although... there's one case where I can't resist,
at least sometimes, and that's this:

if obj.respond_to("meth")
obj.meth
else
<something else>
end

I really dislike the repetition there, and it also technically isn't
thread-safe:

a = Object.new
def a.x; end

Thread.new do
sleep 1
class << a; undef_method("x"); end
end

if a.respond_to?("x")
sleep 2
a.x
end

=> undefined method `x' for #<Object:0x1cd508> (NoMethodError)

I don't know any way around it, though, except:

begin
obj.meth
rescue NoMethodError
<something else>
end

or some variant thereof. (Obviously in true duck-typing cases you
just send the message without this kind of check, but there are cases
where the check makes sense.)

I've toyed with the idea of some kind of special "nack" object that
would be returned on these kinds of method calls, but I don't think it
plays well with method_missing.


David

--
David A. Black
(e-mail address removed)

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
 
J

Joel VanderWerf

Hi --




I agree in general, although... there's one case where I can't resist,
at least sometimes, and that's this:

if obj.respond_to("meth")
obj.meth
else
<something else>
end

I really dislike the repetition there, and it also technically isn't
thread-safe:

a = Object.new
def a.x; end

Thread.new do
sleep 1
class << a; undef_method("x"); end
end

if a.respond_to?("x")
sleep 2
a.x
end

=> undefined method `x' for #<Object:0x1cd508> (NoMethodError)

I don't know any way around it, though, except:

begin
obj.meth
rescue NoMethodError
<something else>
end

This has the unfortunate side effect of conflating NoMethodErrors that
are reported for other method calls besides the original obj.meth but
which occur during that method. What's the best way of handling that?
Compare exception.backtrace depth with the current backtrace depth?

It's not really threadsafe either. By the time <something else> is
executed, #meth could have been defined (though you do know it was
undefined at the time of the method call, which is an improvement over
the #respond_to version).
 
R

Rich

What about something like

{|x| x =3D=3D x.to_i.to_s}

If the string the above block is applied to is not a number, this
should fail, right?

-Rich
 
D

dblack

Hi --


[Did I really indent by only 1? It must have been early in the
morning... :-]
This has the unfortunate side effect of conflating NoMethodErrors that
are reported for other method calls besides the original obj.meth but
which occur during that method. What's the best way of handling that?
Compare exception.backtrace depth with the current backtrace depth?

I guess, but it gets awfully cluttered. I don't know what the best
way is. Too bad exceptions don't have a depth property or
something....
It's not really threadsafe either. By the time <something else> is
executed, #meth could have been defined (though you do know it was
undefined at the time of the method call, which is an improvement over
the #respond_to version).

I guess in a sense anything non-atomic is non-threadsafe, but here at
least you know there won't be a fault-line between knowing whether the
object responds to the message, and asking it to do so. The method
call happens all at once, and then everything else sort of falls away
from that.


David

--
David A. Black
(e-mail address removed)

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
 
J

Jacob Fugal

I guess, but it gets awfully cluttered. I don't know what the best
way is. Too bad exceptions don't have a depth property or
something....

How about something like this:

class Exception
def self.local
primary =3D self
localized =3D Class.new
klass =3D (class << localized; self; end)
klass.send:)define_method, :=3D=3D=3D) do |other|
primary =3D=3D=3D other and other.depth =3D=3D 1
end
localized
end

def depth
self.backtrace.length
end
end

Usage:

begin
bar()
rescue NoMethodError.local =3D> e
end

def bar
foo()
end

begin
bar()
rescue NoMethodError.local =3D> e
end

First rescue catches the undefined bar since it's local, second rescue
doesn't catch the undefined foo since it's not local.

Jacob Fugal
 
M

Matthew Feadler

First, thanks all very much for the lively discussion thus far. It's
proven very useful.

Timothy said:
The Integer() method raises ArgumentError if given an empty,
non-numeric, or otherwise non-well-formed string.

Hmm...apparently not--at least not w/ 1.8.2 on mswin32.

This:

begin
one, two = ARGV.map{ |n| Integer(n) }
rescue ArgumentError
puts "Usage..."
exit
end

puts one
puts two

Outputs:

nil
nil

When fed no command-line arguments. It does work as intended if any of
the args are non-empty strings, however.

Also:

raise "Hey, '#{ arg }' needs to be a number" if !arg.match(/^\d*$/)

I'm a regexp neophyte, without skill or clue. Any suggestions on
tutorials?

Thanks,

-Matthew
 
J

Joe Van Dyk

First, thanks all very much for the lively discussion thus far. It's
proven very useful.



Hmm...apparently not--at least not w/ 1.8.2 on mswin32.

This:

begin
one, two =3D ARGV.map{ |n| Integer(n) }
rescue ArgumentError
puts "Usage..."
exit
end

puts one
puts two

Outputs:

nil
nil

When fed no command-line arguments. It does work as intended if any of
the args are non-empty strings, however.

Also:

raise "Hey, '#{ arg }' needs to be a number" if !arg.match(/^\d*$/)

I'm a regexp neophyte, without skill or clue. Any suggestions on
tutorials?

Dunno about tutorials, but:

the / characters indidicate that whatever is between them is a regular
expression.
^ is the beginning of a line (or string)
\d is a digit (i.e. 0 1 2 3 4 5 6 7 8 or 9)
* means to match 0 or more of the previous thing.
$ is the end of the line.

So, /^\d*$/ means that the entire string from beginning to end is
comprised of zero or more digits. So, a minus sign ('-') or any
non-digit somewhere in the string would fail the check.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,607
Members
45,241
Latest member
Lisa1997

Latest Threads

Top