Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

루비 문자열을 13배 더 빠르게 정리

생각을 코드로 변환할 때 가장 익숙한 방법을 사용합니다. 이것들은 가장 먼저 떠오르는 방법이며 자동으로 찾아옵니다. 정리가 필요한 문자열이 표시되고 손가락으로 결과를 얻을 방법을 입력합니다.

종종 자동으로 입력하는 메소드는 가장 일반적인 Ruby 메소드입니다. #gsub 문자열의 문자를 대체하는 일반적인 방법입니다. 하지만 Ruby는 표준 작업을 위한 보다 전문화된 편리한 방법을 통해 훨씬 더 많은 것을 제공합니다.

저는 Ruby의 풍부한 관용구를 가장 좋아합니다. 왜냐하면 코드를 더 우아하고 읽기 쉽게 만들어주기 때문입니다. 이 풍부함의 이점을 누리려면 코드의 가장 간단한 부분(예:문자열 정리)을 리팩토링하는 데 시간을 할애해야 하며 어휘를 확장하는 데 약간의 노력이 필요합니다. 질문:추가 노력이 그만한 가치가 있습니까?

공백을 제거하는 4가지 방법

다음은 신용 카드 번호를 나타내는 문자열입니다:"055 444 285". 그것을 사용하기 위해 공백을 제거하고 싶습니다. #gsub 할 수 있다; #gsub 사용 모든 것을 대체할 수 있습니다. 하지만 다른 옵션이 있습니다.

string = "055 444 285"
string.gsub(/ /, '')
string.gsub(' ', '')
string.tr(' ', '')
string.delete(' ')
 
# => "055444285"

편의 방식에서 가장 마음에 드는 것은 표현력입니다. 마지막 것은 이에 대한 좋은 예입니다. "공백 삭제"보다 더 명확하지 않습니다. 옵션 간의 균형을 생각하면 성능 문제가 발생하지 않는 한 가독성이 최우선입니다. 그럼 제가 가장 좋아하는 솔루션인 #delete 정말 원인입니다.

위의 예를 벤치마킹했습니다. 이 방법 중 어느 것이 가장 빠를 것 같습니까?

Benchmark.ips do |x|
  x.config(time: 30, warmup: 2)
 
  x.report('gsub')           { string.gsub(/ /, '') }
  x.report('gsub, no regex') { string.gsub(' ', '') }
  x.report('tr')             { string.tr(' ','') }
  x.report('delete')         { string.delete(' ') }
 
  x.compare!
end
성능이 가장 좋은 순서대로 추측합니다. 토글을 열어 결과 보기
Comparison:
  delete:          2326817.5 i/s
  tr:              2121629.8 i/s   - 1.10x  slower
  gsub, no regex:  868184.1 i/s    - 2.68x  slower
  gsub:            474970.5 i/s    - 4.90x  slower

나는 순서에 대해 놀라지 않았지만 속도의 차이는 여전히 나를 놀라게했습니다. #gsub 느릴 뿐만 아니라 독자가 인수를 '디코딩'하는 데 추가 노력이 필요합니다. 단순한 공간 이상을 정리할 때 이 비교가 어떻게 작동하는지 봅시다.

번호 선택

다음 전화번호를 사용하세요. '(408) 974-2414' . 숫자 => 4089742414만 필요하다고 가정해 보겠습니다. . #scan을 추가했습니다. 또한 원하지 않는 것을 모두 제거하려고 하기보다 특정한 것을 목표로 한다는 것을 더 명확하게 표현하는 것이 좋기 때문입니다.

Benchmark.ips do |x|
  x.config(time: 30, warmup: 2)
 
  x.report ('gsub')           { string.gsub(/[^0-9] /, '') }
  x.report('tr')              { string.tr("^0-9", "") }
  x.report('delete_chars')    { string.delete("^0-9") }
  x.report('scan')            { string.scan(/[0-9]/).join }
  x.compare!
end
다시, 순서를 추측한 다음 토글을 열어 답을 확인하세요.
Comparison:
  delete_chars:   2006750.8 i/s
  tr:             1856429.0 i/s   - 1.08x  slower
  gsub:           523174.7 i/s    - 3.84x  slower
  scan:           227717.4 i/s    - 8.81x  slower

정규식을 사용하면 속도가 느려지는데, 이는 놀라운 일이 아닙니다. 그리고 #scan의 표현력을 드러내는 의도 비용이 많이 듭니다. 하지만 Ruby의 전문화된 방법으로 정리를 처리하는 방법을 보고 더 많은 맛을 느꼈습니다.

돈으로

"€ " 하위 문자열을 제거하는 몇 가지 방법을 시도해 보겠습니다. "€ 300" 문자열에서 . 다음 솔루션 중 일부는 정확한 하위 문자열 "€ "을 지정합니다. , 일부는 단순히 모든 통화 기호 또는 숫자가 아닌 모든 문자를 제거합니다.

Benchmark.ips do |x|
  x.config(time: 30, warmup: 2)
 
  x.report('delete specific chars')  { string.delete("€ ") }
  x.report('delete non-numericals')  { string.delete("^0-9") }
  x.report('delete prefix')          { string.delete_prefix("€ ") }
  x.report('delete prefix, strip')   { string.delete_prefix("€").strip }
 
  x.report('gsub')                   { string.gsub(/€ /, '') }
  x.report('gsub-non-nums')          { string.gsub(/[^0-9]/, '') }
  x.report('tr')                     { string.tr("€ ", "") }
  x.report('slice array')            { string.chars.slice(2..-1).join }
  x.report('split')                  { string.split.last }
  x.report('scan nums')              { string.scan(/\d/).join }
  x.compare!
end

승자가 #delete 에스. 하지만 #delete 변형이 가장 빠를 것으로 예상하십니까? 플러스:다른 방법 중 하나가 일부 #delete보다 빠릅니다. 에스. 어느 것입니까?

추측하고 엽니다.
Comparison:
        delete prefix:   4236218.6 i/s
 delete prefix, strip:   3116439.6 i/s - 1.36x  slower
                split:   2139602.2 i/s - 1.98x  slower
delete non-numericals:   1949754.0 i/s - 2.17x  slower
delete specific chars:   1045651.9 i/s - 4.05x  slower
                   tr:   951352.0 i/s  - 4.45x  slower
          slice array:   681196.2 i/s  - 6.22x  slower
                 gsub:   548588.3 i/s  - 7.72x  slower
        gsub-non-nums:   489744.8 i/s  - 8.65x  slower
            scan nums:   418978.8 i/s  - 10.11x  slower

배열을 슬라이싱하는 것조차 #gsub보다 빠르다는 사실에 놀랐습니다. #split 이다. 숫자가 아닌 모든 것을 삭제하는 것이 특정 부분 문자열을 삭제하는 것보다 빠릅니다.

돈을 따라라

숫자 뒤의 통화를 제거합시다. (저는 느린 #gsub 변형.)

Benchmark.ips do |x|
  x.config(time: 30, warmup: 2)
 
  x.report('gsub')                        { string.gsub(/ USD/, '')
  x.report('tr')                          { string.tr(" USD", "") }
  x.report('delete_chars')                { string.delete("^0-9")
  x.report('delete_suffix')               { string.delete_suffix(" USD") }
  x.report('to_i.to_s')                   { string.to_i.to_s }
  x.report("split")                       { string.split.first }
  x.compare!
end

승자간에 무승부가 있습니다. 가장 빠른 2인자가 되기 위해 경쟁할 것으로 예상되는 두 가지는 무엇입니까?

그리고:`#gsub`가 _얼마나_ 더 느린지 맞춰보세요.
Comparison:
delete_suffix: 4354205.4 i/s
to_i.to_s: 4307614.6 i/s - same-ish: difference falls within error
split: 2870187.8 i/s - 1.52x slower
delete_chars: 1989566.1 i/s - 2.19x slower
tr: 1853957.1 i/s - 2.35x slower
gsub: 524080.6 i/s - 13.22x slower

귀하의 요구에 맞는 특별한 방법이 항상 있는 것은 아닙니다. #to_i를 사용할 수 없습니다. 선행 "0"을 유지해야 하는 경우. 그리고 #delete_suffix 통화가 미국 달러라는 가정에 크게 의존합니다.

특수화된 방법은 정밀 도구와 같으며 특정 컨텍스트의 특정 작업에 적합합니다. 따라서 #gsub 바로 우리에게 필요한 것입니다. 다재다능하며 항상 최우선으로 생각합니다. 그러나 처리하기가 조금 더 어려울 수 있으며 종종 예상보다 더 느립니다. 나에게 Ruby의 풍부함은 작업을 매우 즐겁게 만드는 이유 중 하나이기도 합니다. 속도 승리는 좋은 보너스입니다.