W scenariuszu, który piszę, chcę znaleźć długość Fixnum w Ruby. Mogę zrobić <num>.to_s.length
, ale czy istnieje sposób, aby bezpośrednio znaleźć długość Fixnum bez konwersji go na ciąg?Jak określić długość Fixnum w Ruby?
Odpowiedz
puts Math.log10(1234).to_i + 1 # => 4
Można dodać go do Fixnum tak:
class Fixnum
def num_digits
Math.log10(self).to_i + 1
end
end
puts 1234.num_digits # => 4
Za każdym razem, gdy coś profiluję, dostaję niespodziankę. W MRI 1.9.3, 'n.to_s.length' jest szybsze dla dowolnej liczby całkowitej reprezentowanej przez Fixnum. A _lot_ szybciej: Na moim pudełku, 'n.to_s.length' zajmuje gdzieś od trzeciej do połowy czasu metody logarytmicznej, w zależności od długości liczby. Jeśli liczba musi być reprezentowana przez Bignum, to metoda logarytmiczna zaczyna wygrywać. Obie metody są jednak bardzo szybkie i trwają około 6 milisekund (dla metody logarytmicznej) oraz między 0,2 a 0,3 milisekund (dla metody łańcuchowej). –
@WayneConrad: Brzmi jak 'Math.log10' musi mieć dość nieefektywną implementację. Po prostu wypróbowałem prostą metodę, która przechodzi przez tabelę wszystkich mocy 10, które pasują do bitów 32/64, i wykonuje porównanie "> =" dla każdego - było to dotknięcie szybsze niż "Math.log10", ale wciąż wolniej niż 'to_s'. Można go przyspieszyć przez "rozwijanie" binarnego wyszukiwania tej samej tabeli, tak jak rozwijanie pętli (wtedy tabela nie byłaby już potrzebna - te same liczby byłyby zakodowane na stałe w szeregu warunków). –
Uważaj. Powoduje to błąd w przypadku liczb nie dodatnich. – sawa
Jeśli nie chcesz, aby użyć wyrażenia regularnego, można użyć tej metody:
def self.is_number(string_to_test)
is_number = false
# use to_f to handle float value and to_i for int
string_to_compare = string_to_test.to_i.to_s
string_to_compare_handle_end = string_to_test.to_i
# string has to be the same
if(string_to_compare == string_to_test)
is_number = true
end
# length for fixnum in ruby
size = Math.log10(string_to_compare_handle_end).to_i + 1
# size has to be the same
if(size != string_to_test.length)
is_number = false
end
is_number
end
Innym sposobem:
def ndigits(n)
n=n.abs
(1..1.0/0).each { |i| return i if (n /= 10).zero? }
end
ndigits(1234) # => 4
ndigits(0) # => 1
ndigits(-123) # => 3
Mimo że najlepiej oddana pętla jest ładny, nie jest bardzo Ruby i będzie wolny dla dużych liczb, .to_s jest wbudowaną funkcją i dlatego będzie znacznie szybszy. ALMOST uniwersalnie wbudowane funkcje będą znacznie szybsze niż konstruowane pętle lub iteratory.
Ruby 2.4 ma metodę Integer#digits, która zwraca tablicę zawierającą cyfry.
num = 123456
num.digits
# => [6, 5, 4, 3, 2, 1]
num.digits.count
# => 6
Edycja:
obsługi numerów ujemne (dzięki @MatzFan) wykorzystanie wartości bezwzględnej. Integer#abs
-123456.abs.digits
# => [6, 5, 4, 3, 2, 1]
..jeśli jest liczbą całkowitą dodatnią, w przeciwnym razie 'Math :: DomainError'. O wiele więcej Rubiego – MatzFan
Sidenote dla Ruby 2.4+
Pobiegłem jakieś punkty odniesienia na różnych rozwiązań, a Math.log10(x).to_i + 1
jest rzeczywiście dużo szybciej niż x.to_s.length
. comment from @Wayne Conrad jest nieaktualny. Numer new solution with digits.count
jest daleko w tyle, szczególnie w przypadku większych numerów:
with_10_digits = 2_040_240_420
print Benchmark.measure { 1_000_000.times { Math.log10(with_10_digits).to_i + 1 } }
# => 0.100000 0.000000 0.100000 ( 0.109846)
print Benchmark.measure { 1_000_000.times { with_10_digits.to_s.length } }
# => 0.360000 0.000000 0.360000 ( 0.362604)
print Benchmark.measure { 1_000_000.times { with_10_digits.digits.count } }
# => 0.690000 0.020000 0.710000 ( 0.717554)
with_42_digits = 750_325_442_042_020_572_057_420_745_037_450_237_570_322
print Benchmark.measure { 1_000_000.times { Math.log10(with_42_digits).to_i + 1 } }
# => 0.140000 0.000000 0.140000 ( 0.142757)
print Benchmark.measure { 1_000_000.times { with_42_digits.to_s.length } }
# => 1.180000 0.000000 1.180000 ( 1.186603)
print Benchmark.measure { 1_000_000.times { with_42_digits.digits.count } }
# => 8.480000 0.040000 8.520000 ( 8.577174)
Co to jest "długość" Fixnum "? W jakiej reprezentacji? –
@ JörgWMittag: Ile jest cyfr. – Orcris