DEV Community

Cover image for Ruby function design and other stuff I like ranting about
anes
anes

Posted on

Ruby function design and other stuff I like ranting about

Why?

If the posts title looks weird: it is. This is a small rant about code-functionality that makes little to no sense (in my eyes).
This whole article can be summed up like this:
Image description

How?

I was talking to a coworker about getting the numerical value of a character, like you can in Java, because a char stores the number behind that character.
As an example of that behavior is this function, that counts the occurrence of every character:



public static int[] countCharacters(String input) {
  int[] summand = new int[256];

  for (char c : input.toCharArray()) {
    summand[c]++;
  }
  return summand;
}


Enter fullscreen mode Exit fullscreen mode

My naive suggestion was that you could just run number.to_i to convert it to its (what I think is) ASCII number.
The problem with that are cases like converting "49".to_i, where ruby, as a non-typed language, has to convert that to the number the string represents:



irb(main):017> "12".to_i
=> 12
irb(main):018> "12".sum
=> 99


Enter fullscreen mode Exit fullscreen mode

On the one hand there is the "12".to_i, which translates to 12. Fair enough, that makes sense. The other case, where the String doesn't represent a number, it just translates to 0:



irb(main):012> "a".to_i
=> 0
irb(main):013> "fdsgsdf".to_i
=> 0


Enter fullscreen mode Exit fullscreen mode

That leads to some weird (but expected) behaviour:



irb(main):003> summand = []
=> []
irb(main):006> summand["a".to_i] = 'test'
=> "test"
irb(main):008> summand["4".to_i] = 'test'
=> "test"
irb(main):009> summand
=> ["test", nil, nil, nil, "test"]


Enter fullscreen mode Exit fullscreen mode

Now look closely at that first codeblock. You might realize that there are two operations used:



irb(main):017> "12".to_i
=> 12
irb(main):018> "12".sum
=> 99


Enter fullscreen mode Exit fullscreen mode

The weird part is the "12".sum - as ruby loves translating strings to the numbers they represent, you would expect that to translate to something like this:



irb(main):018> "12".sum
=> 12
irb(main):018> "12".sum
=> 3 # Because 1 + 2 = 3


Enter fullscreen mode Exit fullscreen mode

But you get something completely different:



irb(main):018> "12".sum
=> 99


Enter fullscreen mode Exit fullscreen mode

And now why does this happen you might as?
Because of this:



irb(main):027> "2".sum
=> 50
irb(main):028> "1".sum
=> 49


Enter fullscreen mode Exit fullscreen mode

Because it takes their ASCII representation, the same as java does!

Okay, so shouldn't this work for accessing arrays?

This (obviously) doesn't work out of the box, as you need to convert the string to a number:



irb(main):004> summand["1"] = 'test'
(irb):4:in `[]=': no implicit conversion of String into Integer (TypeError)

summand["1"] = 'test'
        ^^^^^^^^^^^^^
    from (irb):4:in `<main>'
irb(main):005> summand["a"] = 'test'
(irb):5:in `[]=': no implicit conversion of String into Integer (TypeError)

summand["a"] = 'test'
        ^^^^^^^^^^^^^
    from (irb):5:in `<main>'


Enter fullscreen mode Exit fullscreen mode

What about scientific notation?

"What about scientific notation?" you might ask. That's also what I was wondering:



irb(main):022> 2e10
=> 20000000000.0
irb(main):024> "2e10".to_i
=> 2


Enter fullscreen mode Exit fullscreen mode

?!

Yes, you are seeing this right: It converts the "2e10" to a 2, as it's all the numbers until the first non-number. This behavior is seen in longer strings too:



irb(main):036> "21f10".to_i
=> 21


Enter fullscreen mode Exit fullscreen mode

Here it cuts the to_i operation after reaching the f.

Actually converting scientific notation to a number

Now, how can we make this work? This is how:



irb(main):037> "2e10".to_f.to_i
=> 20000000000


Enter fullscreen mode Exit fullscreen mode

And this only works, as numbers of scientific notation have the class Float per default:



irb(main):038> 2e10.class
=> Float


Enter fullscreen mode Exit fullscreen mode

For a dynamically-typed language it still seems to be unable to do a lot of casting for you!

Where is this leading?

Nowhere. Everything I mentioned before is really obscure and won't be important 99% of the time. This article is just for those 1% of cases.

Top comments (1)

Collapse
 
schmijos profile image
Josua Schmid

How code communicates is a very interesting topic. I've got some comments on this and I hope that they are useful:

  • Ruby is a dynamically-typed language. Things often work implicitly and not explicitly. It's the code writer's responsibility to make it look understandable to the reader. Statically-typed languages like Java are always more explicit by design already and pay for the the tradeoff with verbosity.
  • You completely missed String#sum in your explanation. Your argument against Ruby here would be better made against the naming of the checksum tooling.
  • Java has its own flaws in the regard of communication even if it's always more explicit. For example try to compare an int with an Integer. int from -127 to 128 are stored in a lookup table for efficiency. Or did you ever ask yourself why it has a NullPointerException? There are no pointers (anymore).