In Ruby:
123.to_s[0].to_i # => 1
87.to_s[0].to_i # => 8
0.to_s[0].to_i # => 0
What’s the Crystal equivalent code?
In Ruby:
123.to_s[0].to_i # => 1
87.to_s[0].to_i # => 8
0.to_s[0].to_i # => 0
What’s the Crystal equivalent code?
123.to_s[0…0].to_i
Thanks @bcardiff.
Correction: to_s[0..0]
instead of to_s[0...0]
(2 dots, not 3).
I searched all over the docs for to_s
but couldn’t find this example.
Where is this shown/explained?
Let me say that the net result of the expressions is the same in Crystal.
String#[0]
returns the first character as a Char
, and if the char is a digit in base 10 Char#to_i
returns it as an integer, which is guaranteed to be the case because we start from non-negative integers:
% crystal eval '[123, 87, 0].each {|n| p n.to_s[0].to_i}'
1
8
0
The inner workings are different because String#[0]
returns a string in Ruby (nowadays), and because String#to_i
does not raise as Char#to_i
could do with an arbitrary receiver.
So, the given examples work the same in Crystal, but whether that really matters depends on the real usage of that idiom.
I would reach for something like
struct Int
def first_digit
n = self
while n >= 10
n //= 10
end
n
end
end
pp 123.first_digit
pp 87.first_digit
pp 0.first_digit
Since version 2.4 Ruby has Integer#digits
, so idiomatic Ruby would be 123.digits.first
.
Maybe Crystal could have such method too.
Note however 123.digits
return [3,2,1]
, not [1,2,3]
. So it should be 123.digits.last
to get 1
as in the original example.
Good catch, thanks!
For the record: The Ruby code has exactly the same results in Crystal. You don’t need range literals to specify the first character, a simple integer index works.
I’ve actually given coding exercises to candidates where finding arbitrary digits is a useful path to solving it. This was the most elegant solution one candidate submitted (translated to Crystal):
struct Int
def digit(place, base = 10)
digit_count = Math.log(self, base).floor.to_i + 1
if place > digit_count
raise "Can't find digit #{place} of #{self}"
end
self // (base ** place) % base
end
end
123.digit(2)
Also it looks like the Crystal parser for syntax highlighting doesn’t distinguish the //
integer-division operator from an empty regex.
If I remember correctly the Ruby discussion boiled down to this.
Since a decimal integer 123456789
can be decomposed to its base 10 exponents: 9*10^0 + 8*10^1 +..+ 1*10^8
, the exponents correspond to the array index, so you can do n.digits[d] to easily get the digit you want. The array is also easier to generate by just slicing off the LSD and shifting, as the algorithms show.
Thus, Ruby went with 123.digits => [3, 2, 1]
as 43210.digits[0] => 0
makes sense, as there’s always an LSD as n[0] * 10^0
.
Also in Ruby digits
doesn’t work for negative integers (which it shouldn’t conceptionally, because the negative sign is not a digit), so to get the digits of the number you just do n.abs.digits
, and go forward from there.