From: MonkeeSage Date: 2007-11-27T16:20:04+09:00 Subject: Re: FEATURE SUGGESTION: Accept default value for to_f and to_i On Nov 26, 11:46 pm, Mr Magpie wrote: > The benefits my suggestion provides are : > 1) allows an application specific default (of any type) to be supplied, > reducing code required. > 2) allows bad input to be unambigously detected, which (can distinguish > "fds".to_i from "0".to_i) > 3) because the result of to_i always evaluates to true, you can't do > num.to_i ? 'valid int' : 'invalid int' > but with my sugestion you could do > num.to_i(false) ? 'valid int' : 'invalid int' > 4) would be a miniscule change to the existing optimised C unlike some > monkey patch I could do > 5) would avoid performance-sapping exceptions > 6) would avoid expensive regular expressions > 7) as a default parameter, wouldn't affect existing code. I could be dense; well, I probably am. No, I'm sure about it. ;) But let me give it a go anyhow... All of the functionality you mention can be had now, it's just that it wouldn't be as fast. So most of the points are moot. Only 5 & 6 remain. Also, 7 isn't exactly true, since it would require a extra compare operation in the back-end to see if a default was given and return that, or else return 0. But that is probably negligible. Regarding 5 & 6. I benchmarked some code against the default to_i/_f. Here are the code and the results: $ cat test.rb && ./test.rb #!/usr/bin/env ruby class String def to_i2(default=0) Integer(self) rescue default end def to_f2(default=0) Float(self) rescue default end def num? self =~ /^[-+.0-9]+$/ end def to_i3(default=0) self.num? ? self.to_i : default end def to_f3(default=0) self.num? ? self.to_f : default end end require 'benchmark' s1 = "10" s2 = "10a" s3 = "1.0" s4 = "1.0a" n = 1000000 Benchmark.bm { |x| x.report("to_i valid ") { n.times { s1.to_i } } x.report("to_i invalid ") { n.times { s2.to_i } } x.report("to_f valid ") { n.times { s3.to_f } } x.report("to_f invalid ") { n.times { s4.to_f } } x.report("to_i2 valid ") { n.times { s1.to_i2 } } x.report("to_i2 invalid") { n.times { s2.to_i2 } } x.report("to_f2 valid ") { n.times { s3.to_f2 } } x.report("to_f2 invalid") { n.times { s4.to_f2 } } x.report("to_i3 valid ") { n.times { s1.to_i3 } } x.report("to_i3 invalid") { n.times { s2.to_i3 } } x.report("to_f3 valid ") { n.times { s3.to_f3 } } x.report("to_f3 invalid") { n.times { s4.to_f3 } } } user system total real to_i valid 1.160000 0.110000 1.270000 ( 1.307932) to_i invalid 1.180000 0.100000 1.280000 ( 1.318455) to_f valid 1.570000 0.190000 1.760000 ( 1.788322) to_f invalid 1.980000 0.090000 2.070000 ( 2.105102) to_i2 valid 2.310000 0.350000 2.660000 ( 2.703812) to_i2 invalid 39.640000 1.240000 40.880000 ( 42.264511) to_f2 valid 2.880000 0.310000 3.190000 ( 3.377140) to_f2 invalid 40.680000 1.100000 41.780000 ( 43.211592) to_i3 valid 6.470000 0.390000 6.860000 ( 6.975072) to_i3 invalid 3.400000 0.350000 3.750000 ( 3.959219) to_f3 valid 7.250000 0.320000 7.570000 ( 7.605764) to_f3 invalid 3.600000 0.380000 3.980000 ( 4.005525) As you can see, you were correct about point 5 when it is the exceptional case; however, regarding point 6, performance is close to within an order of magnitude of the built-in versions of to_i/_f. That's not too awful. If I may make three counter-points against your suggestion: 1.) It is wierd and completely unintuitive for to_i to return anything *other than integer*! Maybe it's just me, but that would be like calling to_a and getting back a String. Holy return types Batman, what gives? 2.) Would a non-zero default really be used enough (or in cases where the speed of using something like the code I listed above with regexps is not fast enougg) to warrant inclusion? Do you have any real world examples that are not just corner-cases? 3.) (Like Ara said...) If you're worried about the performance of exceptions, how helpful is it to do something like: "10a".to_i(nil) % 2? That's either going to terminate with a NoMethodError, or you'll have to rescue it (eating just as much cycles). Regards, Jordan