Ruby Set: Разница без учета регистра - PullRequest
0 голосов
/ 27 мая 2020

Как мы можем добавить новый метод difference_insenitive к Set, чтобы он игнорировал регистр при сравнении и сохранял результат в исходном регистре? Например:

Обычный Set#difference работает так

=> Set[:A, :B, :C].difference(Set[:a, :B])
=> #<Set: {:A, :C}>

Я хотел получить

=> Set[:A, :B, :C].difference_case_insensitive(Set[:a, :B])
=> #<Set: {:C}>

Я хочу сохранить оригинал Set#difference так что я могу сравнивать и без игнорирования случая.

1 Ответ

4 голосов
/ 27 мая 2020

Повторно откройте класс Set

Вы можете создать небольшой синтаксис c сахар, повторно открыв класс Set и добавив пару служебных методов. Уловка действительно заключается в обработке крайних случаев, таких как объекты, которые не отвечают на #downcase (например, Integer, Float или NilClass). Например, используя Ruby 2.7.1:

class Set
  def difference! other_set
    s1 = downcase self
    s2 = downcase other_set
    s1 - s2
  end

  protected

  def downcase set
    set.map { _1.respond_to?(:downcase) ? _1.downcase : _1 }
  end
end

В следующих примерах используется модифицированный класс Set для иллюстрации некоторых типичных результатов:

Set[:A, :B, :C].difference! Set[:a, :B]
#=> [:c]

Set[:A, :B, :C, 1, nil].difference! Set[:a, :B]
#=> [:c, 1, nil]

Set[:A, :b, :c, 1, nil, 'foo'].difference! Set[:a, :B, "FOO"]
#=> [:c, 1, nil]

Если вы предпочитаете использовать #upcase вместо этого установленная разница возвращает :C вместо :c, go прямо вперед. Точно так же, если вы действительно предпочитаете использовать Set#difference_insensitive вместо Set#difference!, вы, безусловно, можете. Мне больше нравится использовать метод взрыва для этого варианта использования, но именование вещей - это другое сложная вещь в информатике.

Предостережения

Сравнения может быть сложно. Независимо от того, используете вы верхний или нижний регистр, результаты потенциально могут быть наполовину неверными, поскольку символы верхнего и нижнего регистра - это разные объекты. Поскольку :A и :a - это две разные метки с двумя разными регистрами символов, какой из них он должен возвращать как «другой» символ?

Он также не работает, как можно было бы ожидать от многосимвольных метки или объекты String, когда преобразование верхнего или нижнего регистра приводит к отсутствию различий. Например, следующее верно, но не интуитивно понятно, потому что все значения преобразуются в :foobar.

Set[:foobar, :fooBar, :fOoBaR].difference! Set[:FOOBAR, :foobar]
#=> []

Это может иметь отношение или не иметь отношения к вашему варианту использования. Однако, даже если это сработало, все равно возникает вопрос о том, какое написание idosyncrati c должно быть возвращено при сравнении регистров символов.

В зависимости от ваших реальных данных вам может потребоваться копаться в Установите # compare_by_identity , Set # classify и Comparable для различных способов определения / сравнения ваших данных Set способами, которые могут обойти эту проблему. Если вы go этот маршрут, вам также может потребоваться повторно открыть класс Set для определения <=>, но по сути не очевидно, как лучше всего реализовать это, если у вас нет четких ожиданий насчет как вы планируете сравнивать разнородные объекты за пределами примеров данных, которые вы разместили в исходном вопросе.

...