DevHeads.net

numeric?

I've found a bit of an annoyance trying to find out if a number is numeric
or not. I thought it would probably be a simple string method but I found
far more contrived solutions like regex matches. Eventually I settled on
monkey patching string with a Numeric? method using Float(self)! = nil
rescue false.

Is there a better way to go about this? If not, can we get something added
to the core?

Thanks for your time,

Brandon

Comments

Re: numeric?

By Andreas S. at 01/02/2013 - 03:57

A common example:

while new_sock = sock.accept
Thread.new(new_sock) do |s|
... do stuff with 's'
end
end

If you just use new_sock directly within the thread, then its value may
change before you use it, because if a new incoming connection arrives
the outer loop will reassign a new value to it.

Re: numeric?

By Andreas S. at 12/27/2012 - 17:13

I think we need more information, the return values are fixeds? what are
they?

Re: numeric?

By Brandon Weaver at 12/27/2012 - 17:32

Sorry, I wasn't very clear. I was typing most of that from my Phone.

I'm writing scripts for a wireless ISP, and we use several different types
of antennas. They're monitored in several different ways,
SSH/Telnet/SNMP/etc. Annoyingly they all return different values for null,
but they're always string based, so a numeric evaluation will quickly yield
whether the device is working or down. I could write a more correct
solution in the sense that it's less of a hack with individual classes for
every antenna, but the goal is that if we ever get a new type of equipment
that it will take minimal coding to get a framework up and running for it.

The main concept here is making the code as DRY as possible, and to
abstract the testing as far from the hardware as possible so as to give
more general statistics for the entire network. I'm not particularly good
at articulating this one.

One such example is min, max, and avg for multiple fields such as Signal to
Noise, Retransmit, and etc. I used instance_variable_get to dynamically
evaluate fields. There are ways in which I could probably use more of
Enumerator, and I still need to sort that out some more.

On Thu, Dec 27, 2012 at 4:13 PM, Damián M. González <lists@ruby-forum.com>wrote:

Re: numeric?

By Robert Klemme at 12/27/2012 - 18:27

On Thu, Dec 27, 2012 at 11:32 PM, Brandon Weaver
< ... at gmail dot com> wrote:
What's wrong then to use Float() as suggested?

begin
f = Float(from_device)
printf "Read value %20.5f\n", f
rescue ArgumentError => e
# not a float
end

An alternative would be to convert non numeric values to nil - if you
want safe null handling:

f = Float(from_device) rescue nil

You could use Structs and then:

irb(main):001:0> S=Struct.new :a, :b, :c
=> S
irb(main):002:0> S.members
=> [:a, :b, :c]
irb(main):003:0> S.new.members
=> [:a, :b, :c]
irb(main):004:0> s = S.new 1,2,3
=> #<struct S a=1, b=2, c=3>
irb(main):005:0> s.members
=> [:a, :b, :c]
irb(main):006:0> s.each_pair {|k,v| printf "%p=%p\n", k, v}
:a=1
:b=2
:c=3
=> #<struct S a=1, b=2, c=3>

Kind regards

robert

Re: numeric?

By Brandon Weaver at 12/27/2012 - 19:00

I've already sorted them into classes and objects, so instance variables
are created for every antenna and other such things.

On Thu, Dec 27, 2012 at 5:27 PM, Robert Klemme
< ... at googlemail dot com>wrote:

Re: numeric?

By Robert Klemme at 12/28/2012 - 06:40

On Fri, Dec 28, 2012 at 1:00 AM, Brandon Weaver < ... at gmail dot com> wrote:
The more I think about it the more I do believe it is responsibility
of the "driver" (i.e. the bit that fetches the data) to return _proper
data_. In this case it would mean: do not return numbers as strings.
If values are invalid return a special value for that; this value
could be nil or one or more symbols depending on how many states need
to be represented.

The using code should expect data properly typed so it can do its business.

Another thought: if you create instance variables on the fly for
sensors then a Hash is probably a better alternative. You could use a
specific class as values which encapsulates the data along with meta
data (valid, available, taken at...). Example

SensorData = Struct.new :name, :value, :taken_at do
def valid?
value # nil is invalid
end
end

Kind regards

robert

Re: numeric?

By Andreas S. at 12/28/2012 - 08:01

Yes that ensure a proper work, but he says that at any time can enter a
new antenna, and he wants to quickly put it working, so what we know is
that all the null values are returned as strings. Perhaps just the null
values are always returned as strings, the others values meaby are
returned as Fixnums, so he wants to handle strings.

Re: numeric?

By Robert Klemme at 12/28/2012 - 09:10

On Fri, Dec 28, 2012 at 2:01 PM, Damián M. González
<lists@ruby-forum.com> wrote:
And where is the problem with my approach? Does it prevent getting
new devices to work quickly?

I'd rather not speculate but hear Brandon's comment on this. Also,
even if what you say is true it still does not prevent the "driver"
layer to convert "null" strings into nil or something other meaningful
value.

Cheers

robert

Re: numeric?

By Brandon Weaver at 12/28/2012 - 12:10

It uses ssh and SNMP to retrieve data. They return different strings, all
some variation of null, if the antenna is unreachable from the Au. I
decided to opt out of a multi conditional approach to every possible
variation of the word null.

Re: numeric?

By Intransition at 12/28/2012 - 14:59

Re: numeric?

By Tony Arcieri at 12/27/2012 - 13:53

Float(string) is your best bet. Why is this inadequate? Are you trying to
decide if a string "looks like a number" but don't want to work with it as
a number after that?

On Thu, Dec 27, 2012 at 10:40 AM, Brandon Weaver < ... at gmail dot com>wrote:

Re: numeric?

By Brandon Weaver at 12/27/2012 - 13:58

I can't control null returns on certain statements considering it retrieves
data from live devices. If a live device is down it returns null or
similar. There are enough different devices that report different variants
of null that in order to be more flexible, numeric is my most viable
option.

Re: numeric?

By Brandon Weaver at 12/27/2012 - 14:10

Trust me, I wish there was a cleaner way to handle this instead of this
inelegant hack I have to make.

To clarify, I work at a WISP that has several different brands of antennas.
In order to have an effective and DRY solution, I have to be very abstract.
Unfortunatelly the way null or empty is reported is only consistently a
string, making numeric as clean as I can get.

If anyone has a better idea I would love to hear it. I come from a C
background so I still have some bleeding effects from the transfer.

Re: numeric?

By Jonan Scheffler at 12/27/2012 - 13:50

You might be better off to ask yourself why you need the type checking
anyway. If you have a method designed to handle an argument that might be a
string or an integer you might be trying to do two different things.

If you're sure you want the integer you could just to_i the input.

Re: numeric?

By Peter Hickman at 12/27/2012 - 13:56

"fred".to_i => 0
"69skiddoo".to_i => 69
"".to_i => 0

I suspect that these would not be considered numbers.

Re: numeric?

By Andreas S. at 12/27/2012 - 23:22

In these cases, I just double convert i.e.

irb(main):016:0> buffer = 'fred'
=> "fred"
irb(main):017:0> buffer.to_i.to_s == buffer
=> false

And then
irb(main):001:0> buffer = '42'
=> "42"
irb(main):002:0> buffer.to_i.to_s == buffer
=> true

Re: numeric?

By Andreas S. at 12/27/2012 - 23:41

Have to say that that's very cleaver. Very good solution.

Re: numeric?

By Robert Klemme at 12/28/2012 - 06:36

On Fri, Dec 28, 2012 at 5:41 AM, Damián M. González
<lists@ruby-forum.com> wrote:
Sorry to disagree. There are more clever solutions:

Use an Integer and deal with the exception.

1. immediately

irb(main):001:0> buffer = 'fred'
=> "fred"
irb(main):002:0> i = Integer(buffer)
ArgumentError: invalid value for Integer(): "fred"
from (irb):2:in `Integer'
from (irb):2
from /usr/bin/irb:12:in `<main>'
irb(main):003:0> begin; i = Integer(buffer); puts "int"; rescue
ArgumentError; puts "no int"; end
no int
=> nil

2. by converting the value to nil if it is not a number

irb(main):004:0> j = Integer(buffer) rescue nil
=> nil
irb(main):005:0> if j; puts "int"; else puts "no int"; end
no int
=> nil

Kind regards

robert

Re: numeric?

By Brandon Weaver at 12/27/2012 - 23:33

I've seen a similar solution as well. I've just monkey patched string with
the float conversion.