Как отсортировать по двум параметрам в OCL?
Мне нужно отсортировать коллекцию людей по двум параметрам: по фамилиям и по именам. Как я могу сделать что-то подобное в OCL?
3 ответа
sortedBy
Функция сортирует элементы, используя критерии, выраженные в ее теле и <
отношения между каждым собранным результатом.
В вашем случае, если у вас есть surname
атрибут, следующий оператор будет сортировать коллекцию c
с использованием <
оператор на каждую фамилию набрал (так <
по строкам):
c->sortedBy(p | p.surname)
Идея может состоять в том, чтобы вычислить уникальную строку, используя фамилию и объединенное имя вместе. Таким образом, если у вас есть:
- Джордж Смит
- Гарри Смит
- Джордж Смат
Сравнение будет сделано между "Smith_George", "Smith_Garry" и "Smath_George" и будет выполнено в соответствии с лексикографическим порядком:
- Джордж Смат (Sm a th_George)
- Гарри Смит (Smith_G Rry)
- Джордж Смит (smith_G e orge)
Наконец, запрос OCL будет (при условии surname
а также name
как существующие атрибуты):
c->sortedBy(p | p.surname + '_' + p.name)
Этот маленький трюк делает свою работу, но это не "точное" сравнение двух параметров для sortedBy.
Спасибо, ты вдохновил меня. Я только что поднял http://issues.omg.org/browse/OCL25-213 чей текст:
Итерация sortedBy обеспечивает элегантное решение проблемы сортировки, в которой метрика сортировки является проекцией отсортированного объекта. Таким образом, sortedBy(p|p.name) или просто sortedBy(name) являются короткими и избегают возможностей для опечаток из более обычной экспозиции, включающей сравнение двух объектов. Естественное решение вполне может быть эффективным для больших коллекций с нетривиальными метриками.
Однако решение sortedBy незнакомо и так запутанно для новичков и непригодно для многоключевой сортировки, для которой может потребоваться создание искусственного составного одиночного ключа.
Одним из решений может быть предоставление более обычного итератора, такого как sort(p1, p2 | expression-expression), позволяющего сортировку по двум ключам:
sort(p1, p2 | let diff1 = p1.key1.compareTo (p2.key1), если diff1 <> 0, diff 1 else p1.key2.compareTo(p2.key2) endif)
Однако это имеет плохую читаемость и широкие возможности для опечаток.
В качестве альтернативы sortedBy с метрикой с кортежем может поддерживать несколько ключей:
sortedBy (Кортеж {первый = ключ1, второй = ключ2})
(Алфавитный порядок имен частей кортежа определяет приоритет.)
(Поскольку sortedBy декларативно понятен и компактен, неэффективные небольшие / тривиальные реализации могут быть оптимизированы до их эквивалентов sort().)
OCL sortedBy(лямбда), по-видимому, сильно отличается от сортировки Java (компаратор), очевидно, требующей проекции объектов в качестве метрики для сортировки. Однако, если проекция является собственной, у вас есть другая функциональность Java. Поэтому, если вы выполните sortedBy (p | p), сортировка зависит от операции <для p.
Чтобы облегчить это, прототип Eclipse OCL для будущего OCL представляет тип OclComparable с методом compareTo, позволяющим реализовать все реляционные операции при условии, что ваш пользовательский тип расширяет тип OclComparable.
(Подобный OclSummable с zero() и sum () работает в общем, поддерживает Collection::sum(); например, String реализует sum как конкатенацию.)