Извлечение уникальных слов - PullRequest
0 голосов
/ 15 мая 2018

Мне нужно взять имя файла и целое число N и вернуть первые N уникальных слов в данном файле. Допустим, что input.txt имеет это содержание:

I like pancakes in my breakfast. Also, I like pancakes in my dinner.

Результат выполнения этого с N = 13 может быть

I
like
pancakes
in
my
breakfast.
Also,
dinner.

Я знаю, как открывать файл и читать построчно, но кроме этого, я не знаю, как вывести уникальные слова из строк.

1 Ответ

0 голосов
/ 15 мая 2018

Давайте сначала создадим тестовый файл.

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 в качестве получателя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...