Я хотел бы написать метод, возвращающий перечислитель (похожий на тот, который возвращается File::foreach), который возвращает каждую строку файла последовательно, но с перебором. Мой первый подход был

def chomped_lines(filename)
  File.foreach(filename).map(&:chomp).each
end

И использовать его как

my_enum = chomped_lines('my_file.txt')
....
my_enum.each { |line| .... }

Думаю, Enumerator :: lazy можно как-то использовать здесь, но в любом случае код должен работать на Ruby 1.9.3, а это значит, что у меня еще нет ленивых счетчиков.

Как лучше написать такой счетчик?

Кажется, это работает, но если я правильно понимаю (поправьте меня, если я ошибаюсь), файл будет помещен в память в целом в память, чтобы применить map.

2
user1934428 26 Апр 2016 в 13:09

2 ответа

Лучший ответ

Вы можете создать перечислитель следующим образом. Сначала создадим файл для демонстрации.

str =<<_
Now is the
time for all
Rubiests to
come to the
aid of their
bowling team.
_

fname = "temp"
File.write(fname, str)
  #=> 75

IO # foreach без блока возвращает перечислитель :

efe = File.foreach(fname)
  #=> #<Enumerator: File:foreach("temp")> 

Поэтому нам нужно просто встроить этот перечислитель в другой, который перехватывает символ новой строки из строки:

echomp = Enumerator.new do |y|
  loop do
    y << efe.next.chomp
  end
end
  #=> #<Enumerator: #<Enumerator::Generator:0x007fbe128837b8>:each> 

Давай попробуем:

echomp.next
  #=> "Now is the" 
echomp.next
  #=> "time for all" 
echomp.next
  #=> "Rubiests to" 
echomp.next
  #=> "come to the" 
echomp.next
  #=> "aid of their" 
echomp.next
  #=> "bowling team." 
echomp.next
  #=> StopIteration: iteration reached an end

Конечно, вы можете обернуть это методом:

def foreach_with_chomp(fname)
  efe = File.foreach(fname)
  Enumerator.new do |y|
    loop do
      y << efe.next.chomp
    end
  end
end

foreach_with_chomp(fname).each { |s| print "#{s} " }
Now is the time for all Rubiests to come to the aid of their bowling team. 
1
Cary Swoveland 26 Апр 2016 в 17:57