LINUX.ORG.RU

[ООП ГМ]Почему 1.+(2) а не +(1, 2)


0

1

Почему многие дизайны языков программирования предпочитают первый вариант. Ведь операция сложения определена на множестве объектов и интуитивно обладает коммутативностью. Т.е.

"a"+3 == 3+"a"

но с точки зрения убогого ООп это будет

"a".+(3) == 3.+("a")

т.е. нужно либо везде реализовывать оператор сложения с учетом объектов которых еще нет (мало ли что пользователь наваяет сущность кою можно прибавить), либо нарушить единообразие сего оператора, т.е. породить кучу ошибок, либо что делают чаще сделать его нерасширяемым в принципе.

★★☆

Очевидно же! С точки зрения ООП ты шлешь сообщение «+» объекту X с параметром Y. И ессно это штука не коммутативна.

Нотацию +(1,2) поддерживают языки с ООП на generic functions, типа CLOS. Но и там о коммутативности можно забыть.

Macil ★★★★★
()
Ответ на: комментарий от Macil

С точки зрения ООП ты шлешь сообщение «+» объекту X с параметром Y.

С точки зрения рационального дизайна зачем привязывать эту операцию к объекту определенного типа? Или это последствия элегантного дизайна ООП?

wfrr ★★☆
() автор топика

т.е. предлагаете класс (или интерфейс) «+», а «+(a, b)» просто создание нового экземпляра с использованием конструктора (кстати какого? и как добавлять новые конструкторы в класс «+»)?

tkustov
()
Ответ на: комментарий от wfrr

>Или это последствия элегантного дизайна ООП?

Ты меня удивляешь! Вообще-то это тяжелое наследие Симулы. Я думал, это очевидно.

Чтобы сделать как ты хочешь нужна поддержка абстрактных типов данных. А в таком случае ООП как бы и не нужен.

Macil ★★★★★
()
Ответ на: комментарий от tkustov

Это можно организовать с помощью typeclass'ов или принудительной реализации спец. интерфейса (типа Numeric). И тогда класс «+» будет совершенно тривиальной штукой, сделанной на основе генериков.

И по иедее, в современных ООП системах каких-то технических препятствий нет.

Macil ★★★★★
()
Ответ на: комментарий от shamazmazum

так жо как и раньше, только при появлении коммутативности не придется писать


// Дубли
Number{
  String +(String s)
}

String{
  String +(Number n)
}

// 
+(String s, Number n)
Последний вариант требует особого игнорирования порядка аргументов и проч, но позволяет не дублировать код.

wfrr ★★☆
() автор топика
Ответ на: комментарий от shamazmazum

Че там примеры давать. Там все элементарно - методы не привязаны к классам, соответственно, с классами онанировать не надо.

Более того, в CLOS можно определить комбинатор методов, который бы учитывал коммутативность операции(т.е. не нужно было бы определять два разных метода обобщенной функции для разного порядка аргументов).

Love5an
()
Ответ на: комментарий от dave

Да знаю, чё) Вот только работать с ними почти не приходилось. Я на лиспе почти не пишу.

Но на этот раз лисперы могут заслуженно прыгать от счастья и хлопать в ладоши.

shamazmazum
()
Ответ на: комментарий от paranonymous

>это почему?

А с чего ты взял что объекты «2» и «3» должны одинаково реагировать на сообщение «+»? Важным свойсвтом объекта является состояние. Т.е. сообщене «+» по определению не будет «чистым», а значит и о коммутативности мы можем говорить только в частном случае.

В чистой ООП системе все является объектом. А поведение не определяется формально.

Macil ★★★★★
()
Ответ на: комментарий от Macil

> А с чего ты взял что объекты «2» и «3» должны одинаково реагировать на сообщение «+»?

Ну потому что сигнатура и сама функция такая. И например (defmethod add ((a integer) (b integer)) (+ a b)) будет коммутативным методом, который единожды определяется для соответствующих классов, в отличие от классического ооп.

Важным свойсвтом объекта является состояние.

Кто сказал, что состояние объекта меняется при вызове метода? Оно же может и не меняться.

Коммутативность метода несложно получить, если ее хочется.

paranonymous
()

>+(1, 2)

Ты опечатался. Единственно правильно: (+ 1 2)

anonymous
()
Ответ на: комментарий от paranonymous

>Кто сказал, что состояние объекта меняется при вызове метода? Оно же может и не меняться.

Получить можно. Но это не значит что метод будет коммутативен в общем случае и ты не сможешь «выстрелить себе в ногу». Это же касается и CLOS.

А вот допустим в хаскеле... Хотя и там можно заюзать unsafePerformIO

Ну потому что сигнатура и сама функция такая


Сигнатура не определяет интерфейс (по крайней мере не полностью). Ты не забывай что в ООП используется концепция супертипов. И если для Integer'ов это в меньшей степени важно (они обычно нерасширяемые), то в общем случае это крайне важно.

А в динамических языках гарантии вообще нет никакой. Ничто не мешает в любой объект (вольно или не вольно) инжектировать все что угодно.

Macil ★★★★★
()
Ответ на: комментарий от wfrr

Все равно в ООП должен быть обьект, который инкапсулирует реализацию сложения. Является ли этот обьект первым слагаемым или это какой-то висящий в воздухе MySweetObjectAdder - на усмотрение разработчиков языка.

vertexua ★★★★★
()
Ответ на: комментарий от wfrr

Ну я же написал что инкапсулирует, хорошо «на исконном» - прячет, реализацию. И чтобы еще точнее, не обьект, а класс. Вот ты можешь спрятать реализацию за интерфейсом и пользоваться интерфейсом вне зависимости от реализации.

Идея в том, что реализация обьектов должна где-то быть. Концепция - ООП. Значит в обьекте. В каком - дело разрабов языка. То, что операция сложения может по-идее складывать разделы на жестком диске (кошмар), позволяет не бросаться фразами о комутативности и говорить о общей реализации для всех обьектов. Значит нужно определять для каждого обьекта особенное сложение и не позволять складывать обьекты, для которых операция не определена. То, что эта реализация идет вместе с кодом класса - нормально, так как должна соответстовать логике работы этого класса и быть консистетной с ним

И да, перегрузка операторов - зло в чистом виде, его надо запретить раз и навсегда. Пользующихся - расстрелять.

vertexua ★★★★★
()

> «a»+3

Чего-чего? Если рассматривать ЭТО как конкатенацию, то всё становится на свои места =)

yoghurt ★★★★★
()
Ответ на: комментарий от vertexua

> перегрузка операторов - зло в чистом виде

Ну не скажи. Тот же operator* (или как он там выглядит для $your_favorite_language) вполне логично перегрузить для тех же матриц

yoghurt ★★★★★
()
Ответ на: комментарий от vertexua

о, что операция сложения может по-идее складывать разделы на жестком диске (кошмар), позволяет не бросаться фразами о комутативности и говорить о общей реализации для всех обьектов.

Не может, операция сложения не должна менять состояние операндов.

Значит нужно определять для каждого обьекта особенное сложение и не позволять складывать обьекты, для которых операция не определена. То, что эта реализация идет вместе с кодом класса - нормально, так как должна соответстовать логике работы этого класса и быть консистетной с ним

То что один и тотже код копируется по 2 раза это нормально? И операция сложения не должна соответствовать никакой логике работы, не неси чушь. Ни конкатенция строк, ни массивов, ни сложение чисел и неупорядоченных коллекций не зависит от их логики работы. Типичный пример с коллекциями - объединяем элементы, если обе упорядочены то некоммутативно, если хотябы одна неупорядоченна то коммутативно. И неважно List это Queue или Set. При реализации вне интерфеса тебе потребуется всего два метода. Если же следовать твоей логике то на _любую_ реализацию интерфейса Collection потребуется писать как минимум парочку операций сложения (или юзать наследование, что приведет к еще большему гимору, или юзанию костылей типа примесей).

ps. названия интерфейсов из жабки

wfrr ★★☆
() автор топика
Ответ на: комментарий от yoghurt

ага, а как расматривать 3.+(«a») ? Или ты будешь писать реализацию сложения числа и всех возможных типов в классе числа? Я знал что идеи ООП несколько маразматичны, но не до такойже степени.

wfrr ★★☆
() автор топика
Ответ на: комментарий от wfrr

>Или ты будешь писать реализацию сложения числа и всех возможных типов

Визитор как-бы решает

в классе числа?

А, ну да, ещё во всеми обожаемых плюсах операторы можно объявлять вне классов

yoghurt ★★★★★
()
Ответ на: комментарий от wfrr

И да, белочка, что за баттхерт? Можно же определить оператор+(Тип1, Тип2) с реализацией сложения, а оператор+(Тип2, Тип1) выразить через первый

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Визитор как-бы решает

Завязывай с травой, визитор для других целей.

А, ну да, ещё во всех нормальных языках функции можно объявлять вне классов

фикс

wfrr ★★☆
() автор топика
Ответ на: комментарий от wfrr

>визитор для других целей

Ну почему же, та же множественная диспетчеризация по сути. Только вот в языках со статической компиляцией можно перегружать операторы as is, компилятор сам всё разрулит; в Smalltalk-е с его динамической типизацией сложение реализовано через самый что ни на есть визитор

yoghurt ★★★★★
()
Ответ на: комментарий от wfrr

Number - это нечто абстрактное, визитором же можно распределить диспетчеризацию по его сабклассам (SmallInteger, Large{Positive,Negative}Integer, Fraction, Float), как это сделано в одном языке

yoghurt ★★★★★
()
Ответ на: комментарий от Yareg

Конкатенация строк через плюсик - это мерзость, на самом деле =)

yoghurt ★★★★★
()

Касательно синтаксиса же - в сипэпэ можно писать и так и так. Но всё равно надо реализовать правый и левый.

Yareg ★★★
()
Ответ на: комментарий от yoghurt

Не я смолтолк не ковырял, но прозреваю что посещение одного объекта называется иным образом 8)

wfrr ★★☆
() автор топика
Ответ на: комментарий от Yareg

не в синтаксисе дело а в интерпитации.

wfrr ★★☆
() автор топика

Вообще в «говноцэпэпэ» и так принято писать код в одном методе - оператор+=(), а бинарные операции - односрочные. их можно вообще макросом генерировать)

Yareg ★★★
()
Ответ на: комментарий от wfrr

Просто нужно в классе строки создать конструктор от числа, а дальше пусть компилятор разбирается, что вызывать.

Yareg ★★★
()
Ответ на: комментарий от Yareg

>Просто нужно в классе строки создать конструктор от числа, а дальше пусть компилятор разбирается, что вызывать.

Пожалуйста, ради всего святого, не делай так никогда. explicit не зря придумали :)

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Не, ну вообще очевиднее написать string(3)+«a», хоть это и на несколько байт длиннее. Тогда и експлисит можно.

Yareg ★★★
()
Ответ на: комментарий от Yareg

Это безопаснее!

експлисит можно

Нужно! Для каждого конструктора от одного аргумента - в обязательном порядке (исключение - конструктор копирования, но порой и его есть смысл за'explicit'ь)

yoghurt ★★★★★
()
Ответ на: комментарий от Yareg

хотим ли мы «1»+1==«11» или же 2

Глупый вопрос, 2 можно хотеть только в недоязычках типа пыха.

А вообще неочевидные перегрузки не нужны.

Потому и спрашивается сфигали не вынестиреализацию из объектов.

wfrr ★★☆
() автор топика

очевидный фикс: искать метод +() сначала у левого операнда, если его нет, брать из правого

PS завязывай с шишками.

true_admin ★★★★★
()

Ведь операция сложения определена на множестве объектов и интуитивно обладает коммутативностью.

Белка, попробуй на досуге сложить 2 матрицы :)

power
()

> Ведь операция сложения определена на множестве объектов и интуитивно обладает коммутативностью.

Как уже писали, можно определить операцию +(Type1, Type2), а +(Type2, Type1) выразить через первую. Это если таки нужна коммутативность. Одна строчка кода не страшна. Зато есть возможность (по дефолту) таки делать операцию некоммутативной.

А вот операцию * стоит считать коммутативной? Для чисел она коммутативна, а для матриц - нет.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.