Issue #14401 has been reported by tompng (tomoya ishida).

----------------------------------------
Feature #14401: Integer#digitsの逆の動作をするメソッドが欲しい
https://bugs.ruby-lang.org/issues/14401

* Author: tompng (tomoya ishida)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
Integer#digitsの逆の動作をするメソッドがあると良いと思うのですがどうでしょうか?

~~~ruby
inverse_of_digits([5,4,3,2,1]) # => 12345
inverse_of_digits([1,0,1,1], 2) # => 13
inverse_of_digits(num.digits(base), base) == num #=> true
~~~

以下のようなケースで便利です

~~~ruby
# colors
rgba = [0xff, 0x00, 0x00, 0x80]
color_int = inverse_of_digits(rgba, 256)

# encoder, decoder
module Base52
  CHARS = [*(?A..?Z),*(?a..?z)]
  def self.encode str
    num = inverse_of_digits(str.bytes, 256)
    num.digits(52).map{|i|CHARS[i]}.join
  end

  def self.decode str
    digits = str.chars.map { |c| CHARS.index c }
    num = inverse_of_digits(digits, 52)
    num.digits(256).map(&:chr).join
  end
end
~~~

また、rubyでこのメソッドを以下のように作ると実行速度が非常に遅いので、ruby本体に効率的な方法で実装されていると良いのではと思います。

~~~ ruby
def inverse_of_digits digits, base = 10
  num = 0; digits.reverse_each { |n| num = num * base + n }; num
end
inverse_of_digits [9]*1_000_000 # takes about 5 minutes
([9]*1_000_000).join.reverse.to_i; # ends within a second
~~~


## メソッド名

~~~
Integer.from_digits(digits, base = 10)
~~~

が良いと思うのですがどうでしょう?

他思いついた名前:

~~~
Array#revert_digits(base = 10)
Integer(string, base=10) を Integer(string_or_digits, base=10) に変える
~~~


## 実装
以下のアルゴリズムでパッチを作りました。

~~~ ruby
# converting [9, 8, 7, 6, 5, 4, 3, 2, 1] to 123456789
[9, 8, 7, 6, 5, 4, 3, 2, 1] # first, dup the given array
[89, 67, 45, 23, 1, 0, ...] # collapse two adjacent numbers with `base * right + left`,  base=10
[6789, 2345, 1, 0, 0,  ...] # with base=100
[23456789, 1, 0, 0, 0, ...] # with base=10000
[123456789, 0, 0, 0,   ...] # with base=100000000, done. return the first value
~~~

ベンチマーク(to_iとの比較)

~~~ ruby
[1_000, 10_000, 100_000, 1000_000].each do |n|
  d = [9] * n
  s = '9' * n
  puts "n=#{n}"
  t=Time.now; Integer.from_digits(d); puts "digits: #{Time.now-t}"
  t=Time.now; s.to_i; puts "to_i:   #{Time.now-t}"
  puts
end
__END__
n      1000     10000     100000    1000000
digits 7.2e-05  0.001104  0.017904  0.453604
to_i   5.0e-05  0.000594  0.015725  0.451933
~~~

## 他に考慮すべき点
範囲外の数字(負数やbase以上の数)が含まれてる場合はエラー扱いすべきか?

~~~ruby
inverse_of_digits([-1,11,0,1], 10) #=> 1109 or Error?
~~~

個人的にはエラー扱いせず計算してしまっていいんじゃないかと思います。
- エラーださなくてもあまり害はなさそう
- あらかじめ繰り上がり繰り下がり処理をした上で渡すという手間が省けるので便利


---Files--------------------------------
inverse_of_digits.patch (3.04 KB)


-- 
https://bugs.ruby-lang.org/