Getting char from string as fixnum in 1.8 and 1.9

What’s the best way to write this program so that it will run correctly
on both 1.8 and 1.9? This works, but I’m just curious if there’s
anything better, preferably without the RUBY_VERSION test, without
adding methods to String, and without losing much efficiency compared
with 1.8’s String#[].

if RUBY_VERSION =~ /\A1.9/
def third_char_as_fixnum(s)
s[2].ord
end
else
def third_char_as_fixnum(s)
s[2]
end
end

s = “abc”
p third_char_as_fixnum(s) # ==> 99

Hi –

On Tue, 21 Jul 2009, Joel VanderWerf wrote:

else
def third_char_as_fixnum(s)
s[2]
end
end

s = “abc”
p third_char_as_fixnum(s) # ==> 99

I’m sure someone will have something more elegant but I’ll get the
ball rolling with:

def third_char_as_fixnum(s)
s[2,1].unpack(“C*”)[0]
end

It may not pass the not losing much efficiency test, though.

David

David A. Black wrote:

def third_char_as_fixnum(s)
s[2,1].unpack(“C*”)[0]
end

Hm, worth a try, and also:

s[2,1].unpack(“C”).first

Joel VanderWerf wrote:

David A. Black wrote:

def third_char_as_fixnum(s)
s[2,1].unpack(“C*”)[0]
end

About a factor of three slower, as I measure it:

require ‘benchmark’

if RUBY_VERSION =~ /\A1.9/
def third_char_as_fixnum_1(s)
s[2].ord
end
else
def third_char_as_fixnum_1(s)
s[2]
end
end

def third_char_as_fixnum_2(s)
s[2,1].unpack(“C”).first
end

s = “abc”
N = 1_000_000

Benchmark.bmbm(12) do |bm|
bm.report(“1”) {N.times {third_char_as_fixnum_1(s)}}
bm.report(“2”) {N.times {third_char_as_fixnum_2(s)}}
end

END

Rehearsal -----------------------------------------------
1 1.060000 0.000000 1.060000 ( 1.079800)
2 1.620000 0.000000 1.620000 ( 1.634525)
-------------------------------------- total: 2.680000sec

               user     system      total        real

1 0.580000 0.000000 0.580000 ( 0.598929)
2 1.640000 0.000000 1.640000 ( 1.664318)

At 2009-07-20 02:20PM, “Joel VanderWerf” wrote:

end

else
def third_char_as_fixnum(s)
s[2]
end
end

s = “abc”
p third_char_as_fixnum(s) # ==> 99

This works in both 1.9.1 and 1.8.7

 def third_char_as_fixnum(s)
   s[2].ord
 end

Because Integer#ord returns the integer receiver.

[email protected] wrote:

ruby -v -rfoo -e ‘p “foo”[2].ord’
ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-linux]
111

Still meets the constraint: “without adding methods to String”
:slight_smile:

That seems like a pretty good approach, actually, and it happens to be
what the snmp gem does (lib/ber.rb):

Add ord method to Fixnum for forward compatibility with Ruby 1.9

if “a”[0].kind_of? Fixnum
unless Fixnum.methods.include? :ord
class Fixnum
def ord; self; end
end
end
end

Joel VanderWerf wrote:

[email protected] wrote:

cat foo.rb
unless 0.respond_to?(:ord)
class Integer
def ord; self; end
end
end

Still meets the constraint: “without adding methods to String”
:slight_smile:

That seems like a pretty good approach, actually, and it happens to be
what the snmp gem does (lib/ber.rb):

The reason I wanted to avoid adding anything to String was that I
couldn’t think of a method name that would be pretty safe from conflict
without being too verbose or obscure (String#get_nth_char_as_int ?).

It’s hard to imagine Integer#ord ever meaning anything other than this
(it does in 1.8.7 and 1.9.1, as Glenn pointed out), so it seems safe to
import it from future, as they say.

Thanks everyone! (This will help bit-struct run in 1.9.)

Hi –

On Tue, 21 Jul 2009, Joel VanderWerf wrote:

end

Add ord method to Fixnum for forward compatibility with Ruby 1.9

if “a”[0].kind_of? Fixnum
unless Fixnum.methods.include? :ord
class Fixnum
def ord; self; end
end
end
end

There’s always a bit of fragility, since someone else might have added
an ord that does something else… unlikely, of course, but still.

Also, I didn’t suggest adding String#ord because you had said you
didn’t want to add methods to String (though your next post clarifies
what you meant by that). If you want to be more future-esque you could
add it to String instead of Fixnum (though that might involve one of
those unpack-based implementations and therefore not be as fast).

David

preferably without the RUBY_VERSION test, without adding methods to
String, and without losing much efficiency compared with 1.8’s String#[].

This may not be pretty, but you do not need to add anything.
It works on 1.8.6, 1.8.7, and 1.9.0 if byte is good enough.

s[2…2].each_byte{|f| p f}

Harry

On Mon, Jul 20, 2009 at 7:40 PM, Glenn J.[email protected] wrote:

  s[2].ord

This works in both 1.9.1 and 1.8.7

def third_char_as_fixnum(s)
  s[2].ord
end

Because Integer#ord returns the integer receiver.

ruby -v -e ‘p “foo”[2].ord’
ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-linux]
-e:1: undefined method `ord’ for 111:Fixnum (NoMethodError)

cat foo.rb
unless 0.respond_to?(:ord)
class Integer
def ord; self; end
end
end

ruby -v -rfoo -e ‘p “foo”[2].ord’
ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-linux]
111

Still meets the constraint: “without adding methods to String”
:slight_smile: