Я предположил, что это фамилии, которые должны быть уникальными.
Давайте сначала создадим файл.
text =<<~END
Smith, Kim
ut Voluptatem ipsam et at.
Marv, Gardens
non Facere et necessitatibus animi.
McLoughlin, Matt
consequatur Eveniet temporibus ducimus amet eaque.
Smith, Jen
pariatur Unde voluptas sit fugit.
Brad, Nick
et Maiores ab officia sed.
END
FName = "test.txt"
File.write(FName, text)
#=> 239
См. IO :: write 1 .Теперь мы читаем файл и вычисляем количество уникальных фамилий.
require 'set'
File.foreach(FName).with_index.with_object(Set.new) do |(line, idx),set|
set << line[/.+(?=,)/] if idx.even?
end.size
#=> 4
Шаги следующие:
enum1 = File.foreach(FName)
#=> #<Enumerator: File:foreach("test.txt")>
enum2 = enum1.with_index
#=> #<Enumerator: #<Enumerator: File:foreach("test.txt")>:with_index>
enum3 = enum2.with_object(Set.new)
#=> #<Enumerator: #<Enumerator: #<Enumerator:
# File:foreach("test.txt")>:with_index>:with_object(#<Set: {}>)>
См. IO :: foreach , Enumerator # with_index , Enumerator # with_object и Set :: new .Обратите внимание, что enum2
и enum3
могут рассматриваться как составные перечислители .
Первый элемент генерируется с помощью enum3
, передается в блок, а переменным блока назначаются значения:
(line, idx),set = enum3.next
#=> [["Smith, Kim\n", 0], #<Set: {}>]
line
#=> "Smith, Kim\n"
idx
#=> 0
set
#=> #<Set: {}>
line
, idx
и set
являются блочными переменными .Процесс разбиения enum3.next
на три составляющие называется декомпозиция массива .См. эту статью для более полного обсуждения этого важного метода.
Теперь выполняется вычисление блока:
idx.even?
#=> true
s = line[/.+(?=,)/]
#=> "Smith"
set << s
#=> #<Set: {"Smith"}>
См. Целое число # четное? и Set # << </a>.При вычислении s
(третья форма) используется метод Sting # [] с регулярным выражением /.+(?=,)/
, которое гласит: «соответствовать одному или нескольким символам, следующим за запятой», (?=,)
является положительным взглядом .
Второй элемент генерируется enum3
, передается в блок, переменным блока присваиваются значения и выполняется расчет блока:
(line, idx),set = enum3.next
#=> [["ut Voluptatem ipsam et at.\n", 1], #<Set: {"Smith"}>]
line
#=> "ut Voluptatem ipsam et at.\n"
idx
#=> 1
set
#=> #<Set: {"Smith"}>
idx.even?
#=> false
Поскольку idx.even? #=> false
мы пропускаем эту строку. (Действительно, единственная причина для включения with_index
состоит в том, чтобы определить, какие строки имеют четные индексы.) Третий элемент генерируется с помощью enum3
, переданного в блок,переменным блока назначаются значения и выполняется вычисление блока:
(line, idx),set = enum3.next
#=> [["Marv, Gardens\n", 2], #<Set: {"Smith"}>]
line
#=> "Marv, Gardens\n"
idx
#=> 2
set
#=> #<Set: {"Smith"}>
idx.even?
#=> true
s = line[/.+(?=,)/]
#=> "Marv"
set << s
#=> #<Set: {"Smith", "Marv"}>
и так далее, пока мы не получим:
arr = File.foreach(FName).with_index.with_object(Set.new) do |(line, idx),set|
set << line[/.+(?=,)/] if idx.even?
end
#=> #<Set: {"Smith", "Marv", "McLoughlin", "Brad"}>
Обратите внимание, что, поскольку наборы содержат уникальные значения, "Smith"
былоне добавляется в набор при обработке "Smith, Jen"
. Теперь мы выполняем последний шаг:
arr.size
#=> 4
1 Хотя write
является методом IO
, его обычно пишут(и другие IO
методы) с File
как его получатель.Это допустимо, поскольку File
является подклассом IO
и, следовательно, наследует методы последнего.Две двоеточия в IO::write
означают, что write
- это метод класса .Напротив, знак фунта в IO#gets
указывает, что gets
является методом экземпляра .