Давайте сначала создадим тестовый файл.
str =<<END
We like pancakes for breakfast,
but we know others like waffles.
END
FName = 'temp'
File.write(FName, str)
#=> 65 (characters written)
Нам нужно вернуть массив, содержащий первые nbr_unique
уникальные слова из файла с именем file
, поэтому давайте напишем метод, который сделает это.
def unique_words(fname, nbr_unique)
<code needed here>
end
Вам необходимо добавить уникальные слова в массив, которые будут возвращены этим методом, поэтому давайте начнем с создания пустого массива, а затем вернем этот массив в конце метода.
def unique_words(fname, nbr_unique)
arr = []
<code needed here>
arr
end
Вы знаете, как читать файл построчно, поэтому давайте сделаем это, используя метод класса " rel="nofollow noreferrer"> IO :: foreach 1 .
def unique_words(fname, nbr_unique)
arr = []
File.foreach(fname) do |line|
<code need here to process line>
end
arr
end
Переменная блока line
равна "We like pancakes for breakfast,\n"
после прочтения первой строки. Во-первых, символ новой строки должен быть удален. Изучите методы класса
Строка , чтобы узнать, можно ли ее использовать для этого.
Вторая строка содержит слово "we"
. Я предполагаю, что "We"
и "we"
не должны рассматриваться как уникальные слова. Обычно это делается путем преобразования всех символов строки либо в нижний, либо в верхний регистр. Вы можете сделать это для каждой строки или для каждого слова (после того, как слова были извлечены из строки). Опять же, ищите подходящий метод в классе String
для этого.
Далее вам нужно извлечь слова из каждой строки. Еще раз, ищите String
метод для этого.
Затем нам нужно определить, скажем, "like"
(или "LIKE"
), который будет добавлен в массив arr
. Посмотрите на методы экземпляра для класса Array для подходящего метода. Если он добавлен, нам нужно посмотреть, содержит ли arr
теперь nbr_unique
слов. Если это так, нам не нужно читать больше строк файла, поэтому нам нужно вырваться из блока foreach
(возможно, используйте ключевое слово break
).
Есть еще одна вещь, о которой нам нужно позаботиться. Первая строка содержит "breakfast,"
, вторая "waffles."
. Очевидно, мы не хотим, чтобы возвращаемые слова содержали пунктуацию. Есть два способа сделать это. Первый - убрать пунктуацию, второй - принимать только буквы.
Учитывая строку, содержащую пунктуацию (строку или слово), мы можем создать вторую строку, равную исходной строке с удаленной пунктуацией. Один из способов сделать это - использовать метод String # tr . Предположим, что строка "breakfast,"
. Тогда
"breakfast,".tr(".,?!;:'", "") #=> "breakfast"
Чтобы принимать только буквы, мы можем использовать любое из следующих регулярных выражений (все возвращают "breakfast"
):
"breakfast,".gsub(/[a-zA-Z]+/, "")
"breakfast,".gsub(/[a-z]+/i, "")
"breakfast,".gsub(/[[:alphaa:]]+/, "")
"breakfast,".gsub(/\p{L}+/, "")
Первые два работают только с символами ASCII. Третья (POSIX) и четвертая работа ( \ p {} конструкция ) с Unicode (поиск в Regexp ).
Обратите внимание, что более эффективно удалять пунктуацию со строки перед извлечением слов.
Дополнительный кредит: используйте Перечислитель # with_object
Всякий раз, когда вы видите объект (здесь arr
), инициализированный как пустой, управляемый, а затем возвращаемый в конце метода, вы должны рассмотреть возможность использования метода Enumerator#with_object
или (чаще), Enumerable # each_with_object . Оба они возвращают объект, указанный в имени метода.
Метод IO::foreach
возвращает перечислитель (экземпляр класса Enumerator
), когда у него нет блока (см. Документ). Поэтому мы могли бы написать
def unique_words(fname, nbr_unique)
File.foreach(fname).with_object([]) do |line, arr|
<code need here to process line>
end
end
Мы удалили две строки (arr = []
и arr
), но также ограничили область действия arr
блоком. Это не имеет большого значения, но это Руби.
Дополнительные дополнительные кредиты: используйте методы класса Набор
Предположим, мы написали следующее.
require 'set'
def unique_words(fname, nbr_unique)
File.foreach(fname).with_object(Set.new) do |line, set|
<code need here to process line>
end.to_a
end
Когда мы извлекаем слово "we"
из второй строки, нам нужно проверить, должно ли оно быть добавлено в набор. Поскольку наборы имеют уникальные элементы, мы можем просто попытаться это сделать. Мы не сможем этого сделать, потому что set
уже будет содержать это слово из первой строки файла. Удобный способ сделать это: Set # add? :
set.add?("we")
#=> nil
Здесь метод возвращает nil
, что означает, что набор уже содержит это слово.Это также говорит нам, что нам не нужно проверять, содержит ли набор nbr_unique
слов.Если бы мы смогли добавить слово в набор, было бы возвращено set
(с добавленным словом).
Блок возвращает значение set
(набор).Метод Set # to_a преобразует этот набор в массив, который возвращается методом.
1 Обратите внимание, что я вызвал метод класса IO::foreach
, написав File.foreach(fname)...
ниже.Это допустимо, потому что File
является подклассом IO
(File.superclass #=> IO
).Я мог бы вместо этого написать IO.foreach(fname)...
, но чаще используется File
в качестве получателя.