ruby fast reading from std

…衆ロ難τιáo~ 提交于 2020-01-14 06:41:29

问题


What is the fastest way to read from STDIN a number of 1000000 characters (integers), and split it into an array of one character integers (not strings) ?

123456 > [1,2,3,4,5,6]

回答1:


This should be reasonably fast:

a = []
STDIN.each_char do |c|
  a << c.to_i
end

although some rough benchmarking shows this hackish version is considerably faster:

a = STDIN.bytes.map { |c| c-48 }



回答2:


The quickest method I have found so far is as follows :-

  gets.unpack("c*").map { |c| c-48}

Here are some results from benchmarking most of the provided solutions. These tests were run with a 100,000 digit file but with 10 reps for each test.


                                  user     system      total        real
each_char_full_array:         1.780000   0.010000   1.790000 (  1.788893)
each_char_empty_array:        1.560000   0.010000   1.570000 (  1.572162)
map_byte:                     0.760000   0.010000   0.770000 (  0.773848)
gets_scan                     2.220000   0.030000   2.250000 (  2.250076)
unpack:                       0.510000   0.020000   0.530000 (  0.529376)

And here is the code that produced them

#!/usr/bin/env ruby

require "benchmark"

MAX_ITERATIONS = 100000
FILE_NAME = "1_million_digits"

def build_test_file
  File.open(FILE_NAME, "w") do |f|
    MAX_ITERATIONS.times {|x| f.syswrite rand(10)}
  end
end

def each_char_empty_array
  STDIN.reopen(FILE_NAME)
  a = []
  STDIN.each_char do |c|
    a << c.to_i
  end
  a
end

def each_char_full_array
  STDIN.reopen(FILE_NAME)
  a = Array.new(MAX_ITERATIONS)
  idx = 0
  STDIN.each_char do |c|
    a[idx] = c.to_i
    idx += 1
  end
  a
end

def map_byte()
  STDIN.reopen(FILE_NAME)
  a = STDIN.bytes.map { |c| c-48 }
  a[-1] == -38 && a.pop
  a
end

def gets_scan
  STDIN.reopen(FILE_NAME)
  gets.scan(/\d/).map(&:to_i)
end


def unpack
  STDIN.reopen(FILE_NAME)
  gets.unpack("c*").map { |c| c-48}
end

reps = 10
build_test_file
Benchmark.bm(10) do |x|
  x.report("each_char_full_array: ") { reps.times {|y| each_char_full_array}}
  x.report("each_char_empty_array:") { reps.times {|y| each_char_empty_array}}
  x.report("map_byte:             ") { reps.times {|y| map_byte}}
  x.report("gets_scan             ") { reps.times {|y| gets_scan}}
  x.report("unpack:               ") { reps.times {|y| unpack}}
end



回答3:


scan(/\d/).map(&:to_i)

This will split any string into an array of integers, ignoring any non-numeric characters. If you want to grab user input from STDIN add gets:

gets.scan(/\d/).map(&:to_i)


来源:https://stackoverflow.com/questions/3334294/ruby-fast-reading-from-std

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!