3Авг

Сколько стоит новая приора 2018 года: цена, фото, характеристики, видео Priora 2018

Представляем Priora: утилиту приоритезации объектов для Ruby | Элиав Лави | Ruby Inside

TL;DR: я выпустил Priora , новый драгоценный камень, который помогает в удобной расстановке приоритетов коллекций объектов в Ruby! Проверьте это (GitHub/RubyGems).
В этом посте я расскажу о мотивах, дизайне и повестке дня, которые я выбрал при создании этой утилиты. Части этой истории были извлечены в файл README библиотеки на GitHub.

На днях коллега-разработчик спросил мое мнение по поводу одного запроса на включение. Она работала над задачей, которая требовала приоритезации массива объектов в соответствии с определенной бизнес-логикой. Я буду называть класс этих объектов Пост в этой истории. Это класс данных, каждый экземпляр которого знает о author , like_count и is_sponsored :

 class Post 
attr_reader :author, :like_count, :is_sponsored

def initialize(author:, like_ кол-во:, is_sponsored:)
@author = автор
@like_count = like_count
@is_sponsored = is_sponsored
end
end

Мой коллега придумал специальный класс обслуживания для решения задачи расстановки приоритетов и спросил меня, что я думаю об этом подходе. Она объяснила, что столкнулась с некоторыми особенностями и различными деталями, о которых нужно было позаботиться при реализации процедуры расстановки приоритетов, и это заставило ее почувствовать, что выделенный класс был правильным решением. После ознакомления с различными бизнес-требованиями я спросил ее, действительно ли эти объекты сортируются сами по себе: возможно, вместо дополнительного класса для управления приоритетами она могла бы реализовать оператор космического корабля ( <=> ) после Post , а затем просто отсортируйте и переверните массив.

Увы, логика расстановки приоритетов экземпляров Post , требуемая в этой задаче, не была валидной в глобальном масштабе: стало очевидно, что другие применения этого класса имеют другие потребности в сортировке. Следовательно, невозможно было определить <=> по Post . Ruby предлагает Enumerable#sort_by , который принимает блок для предоставления пользовательской схемы сортировки, которая могла бы подходить конкретно для рассматриваемого случая, но затем возникла другая проблема — некоторые атрибуты для определения приоритета массива были логическими. , и они не сортируются из коробки. Другие языки неявно конвертируют true на 1 и false на 0 и, таким образом, разрешить их сортировку; Руби, однако, нет.

Поскольку об исправлении TrueClass и FalseClass не могло быть и речи, другим вариантом было преобразование этих значений ad-hoc. Интуитивно это казалось мне слишком сложным: этот класс приоритизации делал слишком много вещей, а фактические атрибуты для приоритизации были затенены отдаленно связанным кодом. Я сказал своему коллеге, что мне нужно время, чтобы подумать об этом.

Так как я не в первый раз сталкиваюсь с такой проблемой, я подумал, что было бы неплохо иметь служебную библиотеку, которая помогала бы расставить коллекции по приоритетам. В то время как сортировка как концепция хорошо известна благодаря изучению информатики и курсам по алгоритмам, расстановка приоритетов не является ее синонимом, , поскольку она находится на один абстрактный уровень выше; расстановка приоритетов — это использование сортировки для применимых целей («бизнес-логика») таким образом, чтобы наиболее важные объекты в коллекции располагались первыми. Я искал существующее решение на RubyGems и в Интернете, но не нашел того, что искал, поэтому решил записать его сам и придумал Приора .

Мне нужен был простой способ сортировки объектов данных в соответствии с приоритетами, которые в основном представляют собой точки данных, раскрываемые объектами (также известные как геттеры), без хлопот с преобразованием true, false и nil в сортируемые значения. Я также хотел иметь возможность определять приоритеты для класса фиксированным способом, но достаточно гибкий API, чтобы разрешать специальные вызовы со списком приоритетов в качестве параметра.

Priora в действии

Для демонстрационных целей возьмем три Опубликовать экземпляров с различными атрибутами:

 low_like_count_sponsored = Post.new(автор: 'Jay C.', 
like_count: 10, is_sponsored: true)high_like_count_unsponsored = Post.new(автор: 'Aaron R.',
like_count: 90, is_sponsored: false)high_like_count_sponsored = Post. new(автор: 'Don Y.',
like_count: 90, is_sponsored: true)

Используя Priora, мы можем легко расставить приоритеты в коллекции в соответствии с нашими потребностями:

 unprioritized_array = [high_like_count_unsponsored, low_like_count_sponsored, high_like_count_sponsored]prioritized_array = [high_like_count_sponsored, high_like_count_unsponsored, low_like_count_sponsored]Priora.prioritize(unprioritized_array, by: [:like_count, :is_sponsored]) = = приоритетный_массив 
=> true

В этом примере показано, как Priora.prioritize принимает коллекцию без приоритетов и список приоритетов. Последний передается через параметр на . Он ожидает массив символов, геттеров, которые представляют желаемую логику приоритизации. В этом примере мы хотим, чтобы объектов Post с наибольшим количеством лайков были первыми, а спонсируемые посты имели второстепенный приоритет в случае, если два или более объекта Post имеют одинаковые количество лайков .

https://pixabay.com/ru/ropes-ship-barge-sea-nautical-2151683/

В случае, если мы можем зафиксировать приоритет между объектами Post — т.е. нам не нужна гибкость изменения приоритетов каждый раз (чего не было у моего коллеги) — мы можем включить модуль Priora в наш класс и объявить фиксированные приоритеты с помощью макроса класса Prioritize_by и получить более короткий вызов позже. Тогда наш класс будет выглядеть так:

 класс Post 
включает Priora
Prioritize_by :like_count, :is_sponsored attr_reader :author, :like_count, :is_sponsored def initialize(author:, like_count:, is_sponsored:)
@author = author
@like_count = like_count 9000 5 @is_sponsored = is_sponsored
end
end

И получение приоритетного массива будет выглядеть так:

 Priora.prioritize(unprioritized_array) == Prioritized_array 
=> true

Использование Prioritize_by макрос класса повышает читабельность кода за счет гибкости . Используя его, приоритеты объявляются
в классе, и Priora может получать их неявно. В некоторых случаях это может быть правильным выбором, в то время как для других больше подходит явный стиль.

Хотя может показаться, что эта опция определяет оператора космического корабля для включенного класса, на самом деле это не так. Единственное, что он делает, это сохраняет объявленные приоритеты в переменной класса-экземпляра, что позволяет Priora использовать их позже.

В качестве эквивалентного решения можно придумать следующий фрагмент кода:

 unprioritized_array.sort { |a, b| 
[a.like_count, a.is_sponsored ? 1 : 0] <=>
[b.like_count, b.is_sponsored ? 1 : 0] }.reverse == Prioritized_Array
=> true

Что, конечно, правильно. Однако я обнаружил несколько проблем с этим кодом:

  • Он более многословен и подвержен ошибкам.
  • Дважды объявляет логику определения приоритетов. Мы не можем использовать зажигалку sort_by , поскольку логические значения не сортируются по умолчанию.
  • Он обрабатывает преобразование логического значения ( true / false ) в сортируемое значение ( 1 / 0 ) в строке, тем самым смешивая уровни абстракции и вводя в заблуждение потенциального читателя.

Как упоминалось ранее, Priora основана на предположении, что, когда мы говорим о наборе с приоритетом, мы часто имеем в виду результат его сортировки и последующего обращения результата. Это потому, что мы, естественно, думаем о сортировке по возрастанию, от меньшего к большему, в то время как когда мы говорим о «приоритетах» или «высших приоритетах», мы обычно думаем о самых крупных элементах, которые появляются первыми . Когда эти элементы являются объектами данных, мы должны определить, что делает один объект больше другого.

Направленные приоритеты

Очевидно, что это не всегда так, и некоторые процессы приоритизации должны сначала отдавать приоритет более мелким элементам; Приора также поддерживает это. Вы можете изменить направление приоритета для определенного приоритета:

 Priora.prioritize(unprioritized_array, by: [[like_count: :asc], :is_sponsored]) 
=> [low_like_count_sponsored, high_like_count_sponsored, high_like_count_unsponsored]

Мы видим, что пост с низким like_count появляется первым, однако два поста с высоким like_count имеют приоритет is_sponsored , поэтому спонсируемый пост появляется первым.

Как уже отмечалось, для того, чтобы предложить дружественный, ориентированный на предметную логику API, Priora позаботится о преобразовании несортируемых значений, таких как true , false или nil , в сортируемые значения. По умолчанию предполагается, что true больше, чем false и что nil оценивается как 0 .

Вы можете переопределить эти неявные преобразования своими собственными лямбда-выражениями, а также предоставить свои собственные лямбда-выражения для других классов (и, возможно, переопределить их существующую логику определения приоритетов!).

Например, если мы хотим установить приоритет атрибутов класса String по их длине мы могли бы предварительно настроить

Priora соответственно:

 Priora.configuration.add_conversion_lambda(String, lambda { |value| value.length }) 

Конверсионные лямбды также удаляются, если возникнет такая необходимость:

 Priora. configuration.remove_conversion_lambda(String) 

Я следовал тому же правилу, что и раньше, предполагая разумное общее использование: логические атрибуты значение true часто имеют более высокий приоритет, чем атрибуты ложь значений. Это false < true — распространенная идиома во многих других языках программирования. Тот факт, что кто-то может захотеть приоритизировать объекты по логическим атрибутам, подразумевает, что выбор должен быть сделан, поэтому я принял преобладающее соглашение. Я также думаю, что в большинстве случаев это имеет большой смысл. Если это не так, просто явно объявите приоритет как :asc .

Влияние закона ЕС об авторском праве на новости Джулия Приора :: SSRN

Скачать эту статью

Открыть PDF в браузере

Добавить бумагу в мою библиотеку

Делиться: