Прогнило что-то в датском королевстве

Nov 18, 2015 05:10

В новой версии (2.3.0-preview1) руби что-то пошло не так. Разработчики MRI поддержали фичу сравнения хэшей по отношению включения при помощи операторов сравнения. Никого не смутило, что это частично упорядоченное множество. Ок, сравнения классов в языке уже живут очень давно. Но для них хотя бы есть специальный результат сравнения несравнимых ( Read more... )

программирование

Leave a comment

Comments 7

ezz666 November 18 2015, 11:31:39 UTC
Никого не смутило, что это частично упорядоченное множество.
Почему должно было? Это же отношение порядка.

Оно с чистым сердцем говорит {a: 1} <= {b: 2} -- false и {b: 2} <= {a: 1} -- тоже false. Программистская и общечеловеческая интуиция говорит, что a < b - это отрицание a >=b.
Математика с тобой не согласна. Выполнение либо a≤b либо b≤a требуется только для отношения линейного порядка, но совершенно не обязано для частичного.

Я не знаю о чему думает человек который пытается отсортировать частично упорядоченное множество результат же неоднозначен. Особенно если они делают это функцией, преднозначенной для полностью упорядоченных множеств. Это типичный случай undefined behaviour.

Если раньше был эксепшен, такое решение, конечно, нарушает обратную совместимость. Это не слишком хорошо, но случается.

Reply

prijutme4ty November 18 2015, 12:10:05 UTC
Я все понимаю про частично упорядоченные множества, но не часто встречал программы, которые были бы написаны так, чтобы учитывать тот факт, что бывают случаи, когда ни a<=b, ни b<=a не выполнено. Это настолько редкий случай, что никто не заморачивается, это ж программирование, а не математика. Полагаю, что большая часть программистов в рамках ежедневной работы не задумывается о подобных возможностях. А даже если задумывается, то уж точно не предполагает, что два встроенных класса (Hash, Class) по-разному будут обрабатывать несравнимые элементы.

Вот ты часто встречал такие конструкции?
if a == b
elsif a < b
elsif b < a ( ... )

Reply

ezz666 November 19 2015, 00:29:28 UTC
Ну вообще люди об этом думают, достаточно стандарт c++ почитать,
там явно указано, что для соритровок и многих других подобных алгоритмов (бинарный поиск, например) требуется strict weak ordering.

Мало того, в стандартной библиотеке есть stable_sort, который кроме прочего дает однозначный результат, даже если объекты которые, вроде как, равны не совпадают.

Как ты думаешь для кого подобные вещи пишутся?;)

В смысле слабого порядка всё логично: все несравнимые хеши необходимо считать равными:). Это пофиксит и проблемы с твоим примером и с сортировками. Т.е. само по себе введение таких сравнений не страшно.

Другое дело, что существующее определение == не совпадает с соотношением эквивалентности задаваемым <=, что полностью рушит подобную картину. И это уже гораздо более серьёзная проблема ( ... )

Reply

prijutme4ty November 19 2015, 01:32:03 UTC
Strict weak ordering хорош тем, что это единственный оператор, через который выражается всё, в частности, эквивалентность. При этом очевидно, что эквивалентность словарей, как объектов, нельзя задавать как их несравнимость. Так что C++ хотя и позволяет задать несогласованный набор операторов, все равно будет пользоваться одним единственным. Он вообще больше тяготеет к компараторам как внешними объектам, задающим единственную операцию. Это очень здравая практика, но она, увы, никак не вписывается в концепцию руби.

Кстати, я проглядел этот эпик фейл в реализации:
{a: 1, b: 2} <= {a: 1, b: 3}
{a: 1, b: 3} <= {a: 1, b: 2}
Реализация, которая сломала ключевой юзкейс фичи - использование в тестировании. Правда пугает.

А что ты имеешь ввиду, когда говоришь про согласованность <= и ==?

К вопросу про некорректность. Разве a<=b не эквивалентно a < b || a == b? Или это всего лишь !(b < a) ?

Раньше в руби всё делалось оператором <=>, который возвращал положительное, отрицательное или нулевое значение, а остальные операторы опирались на него. ( ... )

Reply


ezz666 November 21 2015, 00:34:14 UTC
Я видел обсуждение на хабре, теперь хочу определиться правильно ли я понимаю решение(я не слишком силен в Ruby). Мы задаём оператор <=> чтобы получить частичный порядок на хэшах. Правильно ли я понимаю, что это решение продиктовано тем, что оператор <=> может применяться к любым объекатм (у нас же динамическая типизация) и случай несравнимости все равно должен быть учтен для объектов разных типов? Т.е. в некотором смысле можно считать, что в Ruby определен частичный порядок для всего у чего определен оператор <=>. И это такая фича языка.

В C++ (на котором я, в основном, пишу и потому с ним сравниваю), такое бы считалось идеолгоически неправильным, т.к. считается что операторы сравнения задают слабый порядок, а функции базовой библиотеки (типа sort) НЕ ДОЛЖНЫ выбрасывать исключение, если оно не пришло из метода объекта, который им подсунули. Штука в том, что исключение это дорого. Именно в такие моменты я понимаю, что c++ это все таки очень низкоуровневый язык.

Reply

prijutme4ty November 21 2015, 03:14:03 UTC
Да, все так. И такое, к моему сожалению, считается идеологически правильным, поскольку язык динамически типизирован и ему не до производительности. Однако как сделать лучше - не понятно.

Оператор == определен вообще у всего. По умолчанию сравнивает object_id, то есть проверяет, что две ссылки указывают на один объект. В подклассах, понятно, переопределен.
Метод <=> определен на всех объектах (ну почти на всех, кроме самого нижнего уровня иерархии, ниже Object). Возвращает он -1,0,1 или nil. В Object он определен так: a<=>b = 0, если a==b, иначе nil. Был миксин Comparable, который при включении в класс давал методы <,<=,==,>,>=,between?, основываясь на результатах метода <=>.

Сравнение хэшей на данный момент реализовано так (то как написано в багтрекере не соответствует действительности): оператор == остался прежним, это строгое равенство пар ключ-значение (не учитывающее порядок добавления ключей в хэш). Оператор <=> НЕ переопределен и имеет ту же реализацию, что у класса Object: возвращает 0 либо nil. Зато появились не привязанные ( ... )

Reply


Leave a comment

Up