LINUX.ORG.RU

ооп, иерархии, подмножества


2

2

если стандартное отношение наследования в точности не совпадает с идеей иерархии/подмножеств (для чего их обычно и пытаются использовать, и потом рождаются псевдопроблемы про квадарт и параллелипипед, эквивалентность итп)... Почему бы не ввести для этого интуитивно ожидаемого отношения специальные ключевые слова и строить все на них? (вместо классического наследования с подразумевающимися принципами [Single responsibility, Open/closed, Liskov substitution, Interface segregation, Dependency inversion], его можно дропнуть вообще) Включая другие интуитивно-нужные вещи типа пересечения. Где-то из ооп-мейнстрима такое уже есть?

★★★★☆

Слишком тяжелые словесные абстрации. Может проще привести небольшой, но показательный кусок кода, а потом всей толпой этот код мы помусолим.

pathfinder ★★★★
()
Последнее исправление: pathfinder (всего исправлений: 1)

Мне другое интересно, как при помощи этих интуитивно ожидаемых отношений делать реальный код? Я половину слов в посте не понял, но скажем есть подмножество A, есть подмножество B, мы строим их объединение получаем подмножество U. Или строим их пересечение, и получаем подмножество C.

Если A и B классы (в смысле С++/Python) то скажем при помощи множественного наследования худо-бедно делается U и он будет даже работать. В питоне я могу сделать извращенное «наследование» а ля пересечение, и на его основе сваять С.

Теперь, если A не класс, и B не класс, то что с этим вообще можно сделать? Что такое A и B? Как вообще из этого самого подмножества получить в итоге что то работающее? Как подмножество устроено?

AIv ★★★★★
()

А что, трудно реализовать абстракцию типа пересечения вручную? Другое дело, если мы хотим, чтобы наши определения органично «вливались» в язык, нам необходимо преодолеть костыльную реализацию этого языка, что в общем случае невозможно. Остается только писать свое подмножество. Мейнстримные ооп языки не проектируются для таких вещей, их задача в том, чтобы облегчить рядовому программисту реализацию тривиальных задач.

anonimous
()

Игроделы давно изобрели такое, например entity component system.

PolarFox ★★★★★
()

и строить все на них?

Хорошее слово - всё, не обозначающее ничего конкретного. Всё что?

квадарт и параллелипипед

Допустим у вас есть такие ключевые слова как вы хотите. Давайте пример с квадратом и параллелепипедом.

no-such-file ★★★★★
()

стандартное отношение наследования в точности не совпадает с идеей иерархии/подмножеств

Кстати, я бы сказал, что это актуально только для крестов. В динамических языках такой проблемы нет.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

квадарт и параллелипипед

Насчет этого не знаю, но эквивалентность на таких абстракциях построить, по-моему, тривиальная задача: Допустим есть два объекта с набором свойств, если множество свойств 1-го и 2-го пересекаются и при этом результат «разницы» их объединения(не знаю как по-научному) равен «нулю», они эквивалентны или даже равны. Пользуясь случаем хотел спросить, может кто знает что такое альфа-конгруэнтность, простыми словами?

anonimous
()
Ответ на: комментарий от AIv

Початай литл лиспер (или скимер) фридмана, там это все на пальцах разжевано, даны все определения.

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

есть два объекта с набором свойств

Методы считаем за свойства?

результат «разницы» их объединения(не знаю как по-научному) равен «нулю», они эквивалентны или даже равны

Ничего, что при одинаковых интерфейсах, внутри объекты могут быть устроены по-разному?

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Методы считаем за свойства?

как угодно, от задачи, но просто это можно реализовать в языке с first-class функциями.

Ничего, что при одинаковых интерфейсах, внутри объекты могут быть устроены по-разному?

Это не имеет отношения к делу, важно, что они возвращают, а ни как они устроены. Т.е. если у нас есть функция EQ, которая определяет эквивалентнось, она должна принимать два аргумента-объекта, которые возвращают ей свои свойства, в виде строк, например.

anonimous
()
Ответ на: комментарий от AIv

Теперь, если A не класс, и B не класс, то что с этим вообще можно сделать? Что такое A и B? Как вообще из этого самого подмножества получить в итоге что то работающее? Как подмножество устроено?

Когда не на что опираться, можно выдумать протоколы/интерфейсы, формальные и не очень. Оригинальная идея ооп вроде была в том, что есть черный ящик (из фабрики) и ему можно слать параметризованные сообщения, а он синхронно возвращает результат (при этом опционально делегирует/форвардит вызов или ругается, что не понял этого сообщения). Когда ооп прибивают к статике, как в одном известном языке, то это все теряет смысл и требуются «классы» и «наследование».

Я понимаю о чем стиви пишет, но могу только как собака смотреть с умными глазами, сказать ничего не получается.

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

Это не имеет отношения к делу, важно, что они возвращают, а ни как они устроены

Очень даже имеет. Дело не только в устройстве, но и в том, что классы делают.

Т.е. если у нас есть функция EQ, которая определяет эквивалентнось, она должна принимать два аргумента-объекта, которые возвращают ей свои свойства, в виде строк, например.

Пусть есть класс A со свойством q и класс B со свойством q, в классе A, q выполняет сложение двух чисел, в классе B, q выполняет умножение двух чисел. Интерфейсы одинаковые, эквивалентны ли данные классы?

no-such-file ★★★★★
()
Ответ на: комментарий от arturpub

Я тоже не могу объяснить текстом... Пока. Вот тред завел, послушать, чо эксперты по всему имеют сообщить.

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от no-such-file

Простите, что влезаю посередине, но: во-первых, почему одно q умножает, а другое делит? Может это p и q? Во-вторых, даже если имена совпали, почему классы A и B *одновременно* 1) не формализовали свои интерфейсы и 2) попали в одну семантическую ситуацию?

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

почему одно q умножает, а другое делит?

2) попали в одну семантическую ситуацию?

Пусть есть абстрактный базовый класс бинарной операции Z и его метод q. A и B реализуют через q сложение и умножение - подмножество множества бинарных операций.

no-such-file ★★★★★
()
Ответ на: комментарий от stevejobs

Я как-то предлагал обсудить идею, которая в теории может упростить или упразднить существующие методы «наследования», но она требует отказаться от текста, и лор ее не поддержал. Надо было не с того наверное начинать.

arturpub ★★
()
Ответ на: комментарий от no-such-file

btw, кроме теории которую тут сейчас надеюсь понапишет quasimoto, можно просто стопнуть конпеляцию и попрость доопределить правила человеку вручную. Типа, впиши вот сюда такую строчку если хочешь это, и вот такую - если хочешь то, и потом нажми кнопку «конпелировать дальше». Человек-то стопудов знает, что «классы» делают.

предлагаю отказаться от слова «классы», кстати, оно сильно завязано на всякое болото. Давайте лучше «модели», например. Модель характеризуется неким характерным набором правил, где «наличие свойства» - часто встречающийся пример правила, и в силу частоты употребления имеющий для себя специальный синтаксис

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

в Jetbrains MPS от текста отказались. В каком-то смысле. И ничо, поциент жив и здравствует... Давай сюда идею )

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от no-such-file

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

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

Что-то подсказывает, что U скорее должно агрегировать AB, чем быть их объединением. Но тут на словах легко быть Львом.

anonymous
()
Ответ на: комментарий от no-such-file

Пусть есть класс A со свойством q и класс B со свойством q, в классе A, q выполняет сложение двух чисел, в классе B, q выполняет умножение двух чисел. Интерфейсы одинаковые, эквивалентны ли данные классы?

конечно нет. Внутри класса, может быть неявно, существуют еще методы mul, sum, - это и есть остаток.

anonimous
()
Ответ на: комментарий от no-such-file

Очень даже имеет. Дело не только в устройстве, но и в том, что классы делают.

Само собой, здесь вопрос, как ты сам определяшь эквивалентность, а в данном случае ты сам определяешь

anonimous
()
Ответ на: комментарий от stevejobs

Я ее там-то не смог сформулировать, блин... Не, начал писать, опять мыслями растекаюсь. Надо как нибудь вырубить нормального дерьма, послушать космос, может попрет.

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

предлагаю отказаться от слова «классы», кстати, оно сильно завязано на всякое болото

Напротив, предлагаю оставить слово классы, причем «как в одном известном языке». Это оставит нас в поле конкретных понятий, т.к. «модели» и «правила» каждый понимает по-своему т.е. никак конкретно. Я вам уже предложил представить что существует кроме слова class, ну пусть слово model. Давайте пример на квадрате и параллелепипеде, что бы вы хотели от ключевого слова model. Давайте иерархию на model, примеры с пересечением и т.п. Просто сейчас пока обсуждать нечего, кроме некоторого ощущения глубокого смысла идеи - этакое «деревянное железо», слова есть, а пощупать нельзя.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

В плане сопровождения кода, да, мы выиграем, но проиграем в плане выразительности и свободы, а так-же простоты.

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

существуют еще методы mul, sum, - это и есть остаток.

Кто сказал? Нет таких методов, есть только q. Значит остатка нет, значит в вашем смысле объекты эквивалентны.

Кстати возможна обратная ситуация, когда интерфейсы формально разные, но по сути одинаковые - два класса с операцией сложения, но в одном классе p, в другом q.

no-such-file ★★★★★
()
Ответ на: комментарий от anonimous

но проиграем в плане выразительности и свободы, а так-же простоты.

Это почему? В части, относящейся к model выдумывайте какой угодно синтаксис, свободный и простой или нет.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

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

anonimous
()
Ответ на: комментарий от no-such-file

Нет таких методов

Их нет на уровне Вашего интерфейса, они просто скрыты от Вас средствами языка, но по сути они есть.

В части, относящейся к model выдумывайте какой угодно синтаксис.

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

anonimous
()
Ответ на: комментарий от no-such-file

объекты эквивалентны

Это всего лишь договоренность. Ты ее нарушаешь, вставив в качестве q в двух местах совершенно разное поведение *и* посылая оба объекта в один цикл обработки. Когда мы говорим об интерфейсе как о единственном способе различать объекты, мы неявно подразумеваем, что совершенно другие объекты с похожим интерфейсом просто не могут попасть в конкретную ситацию, о чем я уже говорил, когда упоминал семантику. Не стану я сувать сокет в список, если знаю, что список потом пойдет в сумматор. Хотя для сокета операция суммирования вполне может быть определена, неважно как и для чего. Динамическая среда позволяет делать то, что не имеет смысла, но к чему бессмысленные примеры.

Еще раз: когда интерфейсы в силу синтаксических причин (и только) совпадают, но должны использоваться в одном месте — их формализуют для отличения. Либо системными средствами, либо средствами самого интерфейса.

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

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

А на «модели» ему не наплевать?

что ты имеешь в виду

Я имею ввиду увидеть конкретный пример иерархии «моделей»/классов для квадрата и параллелепипеда.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

А на «модели» ему не наплевать?

Наплевать. В том то и дело, что ты сам должен написать такой код, чтобы он сделал то что ты хотел.

Я имею ввиду увидеть конкретный пример иерархии «моделей»/классов для квадрата и параллелепипеда.

К примеру, в простейшем случае, ты можешь написать функцию, которая возвращает множество с заданными свойствами, какие ты сам считаешь нужным для того чтобы квадрат был квадратом. Присваиваешь результат переменной, Штампуй эти переменные - они будут «Экземплярами твоего класса»

anonimous
()
Ответ на: комментарий от arturpub

Это всего лишь договоренность. Ты ее нарушаешь, вставив в качестве q в двух местах совершенно разное поведение *и* посылая оба объекта в один цикл обработки

А вы с чего вдруг взяли, что такое разное поведение несовместимо в одном цикле обработки? Я вам уже указал контекст объединяющий q как сложение и q как умножение - подмножество бинарных операций, хотя вообще говоря такой контекст необязателен. Если у меня есть в разных классах метод write, мне совершенно начхать пишет он строку в сокет, на диск, или вообще не пишет, а играет на балалайке полонез Огинского, если такое действие описано в мануале. Когда у меня есть список таких объектов - ок, я пихаю их в один цикл обработки, нет проблем, да здравствует полиморфизм.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Ок, но как это выражает идею ТС'а?

По поводу идеи тса я отписался:

А что, трудно реализовать абстракцию типа пересечения вручную? Другое дело, если мы хотим, чтобы наши определения органично «вливались» в язык, нам необходимо преодолеть костыльную реализацию этого языка, что в общем случае невозможно. Остается только писать свое подмножество. Мейнстримные ооп языки не проектируются для таких вещей, их задача в том, чтобы облегчить рядовому программисту реализацию тривиальных задач.

Это по поводу

но проиграем в плане выразительности и свободы, а так-же простоты.

anonimous
()
Ответ на: комментарий от no-such-file

в «ооп-виде» можно понимать «правила» как автоматические тесты, например. Если класс проходит автотест на него - значит он корректен, не проходит - некорректен. Тесты тестируют реальную идею, а не какое-то ее синтаксическое выражение. Синтаксис может быть вообще любой, в том числе там может не быть никаких class, model, extends итп. Посему важнее сформулировать не какую-то синтаксическую форму, а саму философскую идею, позволяющую оперировать «схемами» объектов (н-р «классами») так, чтобы они напрямую поддерживали иерархию и операции множеств.

этакое «деревянное железо», слова есть, а пощупать нельзя.

множество можно задать перечислением элементов, а можно задать описанием правил. Я не знаю сами элементы, поэтому задаю правила, и предлагаю придумать, что это может быть, чтобы можно было пощупать =)

Например, вот так: если в ооп конструкция «Square extends Poly» неверна, то в нашей некой новой системе «Square is-a Poly with...» будет верно. Что это за система? Можете сформулировать для нее аксиомы корректности, аналогичные SOLID для ооп?

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

Тред целиком сейчас читать лень, но быстрым поиском ключевых слов не нашёл. Идея первопоста похожа на концепции trait и mixin. Определения и литературу можно посмотреть в википедии. Я пользовался mixin в Common Lisp, а trait --- в Perl, объектная система Moose, в частности Moose::Role

anonymous
()

Включая другие интуитивно-нужные вещи типа пересечения. Где-то из ооп-мейнстрима такое уже есть?

ООП-мейнстриму не нужны эти вещи, поскольку все будут писать кто в лес кто по дрова. ООП-мейстрим как раз и хочет воспитать дисциплинированного и предсказуемого программера, который пользуется единым интерфейсом, а не изобретает велосипеды, в чем он, несомненно преуспел. Если бы программисты на ООП_мейнстрим_языках, хотели бы программировать в таком стиле, они без труда могли бы реализовать эти «вещи» средствами самого языка и им не нужны были бы никакие классы. Это говорит о том, что всех все устраивает, а кого не устраивает, пишут на чем то другом, вот и все.

anonimous
()
Ответ на: комментарий от stevejobs

множество можно задать перечислением элементов, а можно задать описанием правил. Я не знаю сами элементы, поэтому задаю правила, и предлагаю придумать, что это может быть, чтобы можно было пощупать =)

Если я Вас правильно понял, Вы имели в виду что-то вроде этого(js):


isAllSidesSame=function(ob){
	if(!ob.sides) return
	if(ob.sides.length===0)return
	var sides=ob.sides
	while(sides.length>=2){
		if(sides[0]!==sides[1]) return false
		sides=sides.slice(1)
	}
	return true
}
isFourSides=function(ob){
	if(!ob.sides)return
	if(ob.sides.length===0)return
	var counter=0, sides=ob.sides
	while(sides.length!==0){sides=sides.slice(1); counter++}
	return counter===4
}
isThisColor=function(ob, wanted_color){
	if(!ob.color) return
	return ob.color===wanted_color
}

makeObject=function(the_sides, the_color){
	return {sides: the_sides, color: the_color}
}
rectangle1=makeObject([1,2,1,2],"green")
rectangle2=makeObject([1,3,1,3],"green")

triangle1=makeObject([1,1,1], "red")
triangle2=makeObject([1,2,3], "red")

square1=makeObject([1,1,1,1],"red")
square2=makeObject([1,1,1,1],"green")

quadrangle1=makeObject([1,2,1,3], "red")
quadrangle2=makeObject([3,2,1,3], "red")

anotherObject=makeObject([1,2,3,4,5,6,7])
anotherObject2=makeObject([], "green")

classOfObject=function(rule){
	var arr=[]
	for(i in global){if(rule(eval(i))){arr.push(i)}} // global in browser - window
	return arr
}


the_class_square=classOfObject(function(x){return isAllSidesSame(x)&&isFourSides(x)})
the_class_quadrangle=classOfObject(function(x){return isFourSides(x)})
the_class_red_quadrangle=classOfObject(function(x){return isFourSides(x)&&isThisColor(x, "red")})

console.log(the_class_square) // --> [ 'square1', 'square2' ]
console.log(the_class_quadrangle)//--> [ 'rectangle1', 'rectangle2', 'square1', 'square2', 'quadrangle1', 'quadrangle2' ]
console.log(the_class_red_quadrangle)//--> [ 'square1', 'quadrangle1', 'quadrangle2' ]
В коде могут быть ошибки, но смысл, думаю, понятен. Это действительно очень сильная и простая система. Можно, к примеру, рандомно нагенерить объектов из заранее заданных свойств, штук 1000, а потом получить их классы. Можно создавать классы прямо в рантайме. Но, повторюсь, ооп-прогерам не нужна реальная мощь. Они лучше будут дрочить на сферические моноиды в вакууме категории эндофункторов.

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

Вообще, по-моему, изначально все эти страусы и трупы навязали плюсовому быдлу какое-то извращенное понимание классов, как «генераторов обьектов». В обычном понимании, классы -это как раз обобщение, а не генерация. Так например если Маша родил Валю, а Даша родила Мишу, при этом оба отпрыска белокурые, это ведь не значит, что Миша есть экземпляр класса Даша, не так ли? Это значит что Валя и Миша относятся к классу блондинов, как в этом коде, собственно.

anonimous
()
Последнее исправление: anonimous (всего исправлений: 1)
Ответ на: комментарий от stevejobs

если в ооп конструкция «Square extends Poly» неверна, то в нашей некой новой системе «Square is-a Poly with...» будет верно

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

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

Если говорить о крестах, то там ооп представляется как выведение конкретных частных классов, выполняющих работу, из некоего абстрактного класса-универсума: Фигура->Прямоугольник->Квадрат->Отрезок->Точка. Причем, поскольку человек не в состоянии сразу охватить всю задачу во всей её сложности, процесс создания такой цепочки становится итеративным. Сначала разрабатывается некое подобие того, что должно быть и постепенно, на каждой итерации рефакторинга строится конечная система классов.

Насколько я смог вас понять, вы предлагаете пойти более интуитивным путем и выводить из простых вещей сложные: Точка->Отрезок->Квадрат->Прямоугольник->Фигура, причем не только с помощью наследования, но и комбинируя какие-то отдельные элементы сложением и вычитанием, что, по вашему мнению, позволит избежать ловушек плюсового ооп. Я правильно вас понял?

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

в ООП квадрат нельзя наследовать от прямоугольника, это два разных класса (древнючий баян, вот что гуглится первым, в текст не вчитывался но вроде о том что надо :-). Дублирующийся код можно вмешивать миксинами, но это плохо рабочие костыли, да и есть они не везде. Я как раз хочу такую систему, где квадрат можно «отнаследовать» от прямоугольника.

stevejobs ★★★★☆
() автор топика
Последнее исправление: stevejobs (всего исправлений: 1)
Ответ на: комментарий от stevejobs

где квадрат можно «отнаследовать» от прямоугольника

Это как раз и есть прототипное ООП

anonimous
()
Ответ на: комментарий от stevejobs

в ООП квадрат нельзя наследовать от прямоугольника

Квадрат - это подтип прямоугольника и подтип ромба. Концепция наследования не позволяет описывать такие вещи. А тайпклассы - позволяют.

Я как раз хочу такую систему, где квадрат можно «отнаследовать» от прямоугольника.

Хотеть не вредно. Вот только называться это будет уже не словом «наследование».

И вообще тут каждый хочет в меру своего разумения свой «идеальный язык». Но все сидят на жопе ровно, а код никто не пишет. Я вот тоже хочу и тоже не пишу. С продвинутой системой типов на тайпклассах, шахматами и поэтессами. И компилятор поверх LLVM.

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

в ООП квадрат нельзя наследовать от прямоугольника

Не то что б нельзя, но не советуют, да. А все потому, что, как я уже сказал, наследование в ооп вообще не подходит для моделирования реального мира. В реальности у вас ни квадрат не наследуется от прямоугольника, ни наоборот. На самом деле квадрат и прямоугольник состоят из отрезков заданных концевыми точками, причем эти отрезки скомбинированы определенными способами.

Т.е. для моделирования этих объектов вам нужен только один класс - точка. Далее определяете класс комбинации точек - фигура. Определяете класс правил комбинирования и отдельные конкретные объекты комбинаторы точек для квадрата и для прямоугольника. В конструктор класса «фигура» передаете нужный объект-комбинатор, который запоминается в классе «фигура». Эквивалентность фигур проверяете в одном смысле сравнением их комбинаторов, в другом смысле сравнением точек. Иерархию фигур строите сбоку путем определения отношения «меньше/больше» для объектов-комбинаторов. Всё, можете писать различные комбинаторы, пересечения/объединения, определять иерархию в том смысле в каком хотите и даже несколько параллельных иерархий. Годится?

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

В реальности у вас ни квадрат не наследуется от прямоугольника, ни наоборот

В реальности абстракцию «прямоугольник» легко можно расширить до квадрата путем введения правила «все стороны равны»

anonimous
()
Последнее исправление: anonimous (всего исправлений: 1)
Ответ на: комментарий от anonimous

В реальности абстракцию «прямоугольник» легко можно расширить до квадрата путем введения правила «все стороны равны»

Вот значит и нужен такой язык, который позволит описать тип квадрата как type Square a is Rectangle a where width(a) == height(a)

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

и что лично ты предлагаешь использовать? практически, сейчас? Жаваскипт с самописным ООП на предикатах как в примере выше?

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

абстракцию «прямоугольник» легко можно расширить до квадрата путем введения правила «все стороны равны»

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

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