Вы можете сделать это с помощью простой однострочной строки, которая позволяет не использовать end_with?
:
Dir[Dir.pwd + '/*.{flac,wv}'].each { |filename| pp filename }
. На ваш вопрос о том, какие значения принимаются end_with?
, ответ заключается в том, что он принимает запятую-ограниченный список значений, например:
'foo'.end_with?('a', 'b', 'c')
=> false
'foo'.end_with?('a', 'b', 'c', 'o')
=> true
Пример документации end_with?([suffixes]+) → true or false
можно прочитать как "end_with?
принимает один или несколько суффиксов, разделенных запятыми, которые будут обрабатываться массивом какпринимающего объекта, и если строка заканчивается каким-либо из суффиксов, тогда возвращается true
, иначе возвращается false
. "
Вы также можете использовать оператор splat для преобразования массива вдопустимое значение для end_with?
:
'foo'.end_with?(*['a', 'b', 'c'])
=> false
'foo'.end_with?(*['a', 'b', 'c', 'o'])
=> true
Обновление для пояснения приведенных примеров
Судя по комментариям ниже, Rich_F был крайне смущен этим ответом оОператор splat, поэтому я поясню свой ответ, чтобы никто не запутался в приведенном мной примере.
Оператор splat *
в Ruby преобразует содержимое массива в запятуюсписок аргументов.Он часто используется при передаче массива в качестве аргумента методу, например:
array = ['a', 'b', 'c']
'foo'.end_with?(*array)
Это "разбивает" массив в список аргументов, разделенных запятыми, и функционально эквивалентно:
'foo'.end_with?(*['a', 'b', 'c'])
Аналогично, он функционально эквивалентен:
'foo'.end_with?('a', 'b', 'c')
Аналогично, он функционально эквивалентен:
array = %w(a b c)
'foo'.end_with?(*array)
Аналогично, он функционально эквивалентен:
'foo'.end_with?(*%w(a b c))
Аналогично, это функционально эквивалентно:
filetypes = %w(.flac .wv)
filename.end_with?(*filetypes)
Учитывая это знание о том, как оператор splat влияет на массивы, и примеры передачи массивов в качестве аргументов в методы, можно сделать вывод, чтоданный пример кода в вопросе может быть изменен следующим образом:
filetypes = %w(.flac .wv)
Dir.open(Dir.pwd).each do |filename|
pp filename if filename.end_with?(*filetypes)
end
Единственное изменение - добавить *
.При добавлении оператора splat *
перед аргументом filetypes
массив filetypes
будет расширен как аргументы с разделителями-запятыми для вызова end_with?
.
Это неэффективное решение дляпроблема состоит в том, что для каждой итерации списка каталогов требуется итерация по массиву filetypes
один раз, а для всех объектов в каталоге - независимо от совпадения, в отличие от использования Dir[Dir.pwd + '/*.{flac,wv}'].each { |filename| pp filename }
, который возвращает только соответствующие файлы и не требует итерациичерез второй массив для каждой итерации.Таким образом, использование end_with?
в данном примере крайне неэффективно и не должно использоваться ни в одном реальном коде.
Например, если задан каталог с 10000 файлов, оканчивающихся на .flac
:
require 'benchmark'
dir = Dir[Dir.pwd + '/*.{flac,vw}']; nil
10.times { puts Benchmark.measure { 10000.times { dir.each { |filename| nil } } } }
4.271809 0.001728 4.273537 ( 4.274684)
4.279765 0.002286 4.282051 ( 4.283524)
4.334877 0.004689 4.339566 ( 4.343982)
4.269334 0.001593 4.270927 ( 4.272033)
4.256148 0.001545 4.257693 ( 4.258734)
4.261371 0.001733 4.263104 ( 4.264229)
4.254568 0.001085 4.255653 ( 4.256379)
4.259886 0.001245 4.261131 ( 4.261711)
4.258024 0.001964 4.259988 ( 4.261133)
4.236385 0.001142 4.237527 ( 4.238184)
По сравнению с исходным примером в вопросе, который более чем в два раза медленнее:
require 'benchmark'
dir = Dir[Dir.pwd + '/*']; nil
filetypes = %w(flac vw); nil
10.times { puts Benchmark.measure { 10000.times { dir.each { |filename| nil if filename.end_with?(*filetypes) } } } }
9.509041 0.003634 9.512675 ( 9.514197)
9.484041 0.003079 9.487120 ( 9.488686)
9.508674 0.002872 9.511546 ( 9.512768)
9.508382 0.002809 9.511191 ( 9.512343)
9.762489 0.011043 9.773532 ( 9.783415)
9.607308 0.005655 9.612963 ( 9.616716)
9.962166 0.009848 9.972014 ( 9.978026)
9.621152 0.005883 9.627035 ( 9.631075)
10.811991 0.010787 10.822778 ( 10.831729)
10.461568 0.013571 10.475139 ( 10.487688)
Разница становится намного более заметной, когда количество совпадающих файлов намного меньше числа несоответствующие файлы.Здесь каталог содержит 1000 .flac
файлов и 9 000 .txt
файлов:
require 'benchmark'
dir = Dir[Dir.pwd + '/*.{flac,vw}']; nil
10.times { puts Benchmark.measure { 10000.times { dir.each { |filename| nil } } } }
0.384366 0.000210 0.384576 ( 0.384669)
0.384336 0.000186 0.384522 ( 0.384717)
0.386674 0.000178 0.386852 ( 0.386947)
0.383575 0.000075 0.383650 ( 0.383671)
0.382555 0.000090 0.382645 ( 0.382692)
0.384618 0.000048 0.384666 ( 0.384677)
0.384687 0.000199 0.384886 ( 0.384976)
0.386517 0.000193 0.386710 ( 0.386842)
0.386167 0.000132 0.386299 ( 0.386388)
0.390683 0.000093 0.390776 ( 0.390817)
По сравнению с исходным примером с использованием оператора splat, который в 36 раз медленнее :
require 'benchmark'
dir = Dir[Dir.pwd + '/*']; nil
filetypes = %w(flac vw); nil
10.times { puts Benchmark.measure { 10000.times { dir.each { |filename| nil if filename.end_with?(*filetypes) } } } }
11.182205 0.014303 11.196508 ( 11.210042)
11.154286 0.011573 11.165859 ( 11.178611)
11.081012 0.009853 11.090865 ( 11.098028)
11.084294 0.009361 11.093655 ( 11.101870)
10.990442 0.007118 10.997560 ( 11.002036)
11.044119 0.009284 11.053403 ( 11.058608)
11.072604 0.009114 11.081718 ( 11.087941)
11.127151 0.009354 11.136505 ( 11.143270)
11.172101 0.012262 11.184363 ( 11.195138)
11.126791 0.010767 11.137558 ( 11.145617)