Re[5]: И все-таки квадрат это прямоугольник
От: Tilir Россия http://tilir.livejournal.com
Дата: 06.03.07 09:49
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, Tilir, Вы писали:


I>

T>>Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.


T>>Можем ли мы придумать для квадрата такое свойство, которого нет у прямоугольника? Да, можем — у квадрата все стороны равны. Для него это provable property Равенство сторон("Квадрат") = true. Очевидно, что существует такой прямоугольник (например со сторонами длиной 2 и 1) для которого Равенство сторон("Прямоугольник") = false => LSP не выполнен и тип "Квадрат" не является подтипом типа "Прямоугольник", что и требовалось доказать.


I>Если в выделенном поменять "Квадрат" и "Прямоугольник" местами, то будет верно. Я серьезно, проверь свое доказательство.


Прямоугольник можно наследовать от квадрата. Примерно так:

class Square{
public:
  explicit Square(int len = 1):m_Len(len){};
  virtual ~Square() {};
  virtual GetSquare() const {return m_Len*m_Len;}
  virtual GetPerimeter() const {return m_Len*4;}
protected:
  int m_Len;
}

class Rectangle: public Square{
public:
  explicit Rectangle(int len = 1, int width = 1):m_Len(len), m_Width(width){};
  virtual ~Rectangle() {};
  virtual GetSquare() const {return m_Len*m_Width;}
  virtual GetPerimeter() const {return m_Len*2 + m_Width*2;}
protected:
  int m_Width;
}


Легко проверить, что тип "Прямоугольник" является подтипом типа "Квадрат" согласно LSP, но не наоборот.
Re[6]: И все-таки квадрат это прямоугольник
От: igna Россия  
Дата: 06.03.07 10:49
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Легко проверить, что тип "Прямоугольник" является подтипом типа "Квадрат" согласно LSP, но не наоборот.


Ну смотри, сам же писал:

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.

Равенство сторон("Квадрат") = true
Равенство сторон("Прямоугольник") = false


Замени q на Равенство сторон, T — на "Квадрат", а S — на "Прямоугольник", и получишь противоречие:

Let Равенство сторон(x) be a property provable about objects x of type "Квадрат". Then Равенство сторон(y) should be true for objects y of type "Прямоугольник" where "Прямоугольник" is a subtype of "Квадрат".


То есть "Прямоугольник" не может быть подтипом "Квадрата".
Re[10]: И все-таки квадрат это прямоугольник
От: anton_t Россия  
Дата: 06.03.07 11:52
Оценка: +2
Здравствуйте, igna, Вы писали:

I>Здравствуйте, anton_t, Вы писали:


_>>А прямоугольник с квадратом лучше не смешивать — поведение разное.


I>Если прямоугольник и квадрат изменяемые. Если неизменные — нет проблем, квадрат это прямоугольник.


Ну, функциональное программирование, вообще, многие проблемы решает
Re[9]: И все-таки квадрат это прямоугольник
От: VoidEx  
Дата: 06.03.07 14:56
Оценка:
Здравствуйте, anton_t, Вы писали:

_>Легко:

_>
_>Button b = new ButtonWithText("bla-bla");
_>

_>А прямоугольник с квадратом лучше не смешивать — поведение разное.
Здесь ButtonWithText, а не Button. То, что вы его к указателю на базовый привели, не сделало его просто Button'ом.

Здравствуйте, FDSC, Вы писали:

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

Button — это не кнопка без текста, это просто кнопка. Я только уточнил, что у нее текста нету, чтобы не говорили о том, что множество всех кнопок шире. Но все равно сказали.
ButtonWithText может участвовать везде, где нужна просто кнопка. А Button там, где нужна ButtonWithText — нет.
Square может участвовать везде, где нужен Rectangle. Rectangle тоже может там, где нужен Square, но при определенных условиях, когда r.a == r.b.
Re[10]: И все-таки квадрат это прямоугольник
От: anton_t Россия  
Дата: 06.03.07 15:10
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>Здравствуйте, anton_t, Вы писали:


_>>Легко:

_>>
_>>Button b = new ButtonWithText("bla-bla");
_>>

_>>А прямоугольник с квадратом лучше не смешивать — поведение разное.
VE>Здесь ButtonWithText, а не Button. То, что вы его к указателю на базовый привели, не сделало его просто Button'ом.

А что такое "Просто Button"?
Re[9]: И все-таки квадрат это прямоугольник
От: MouseEntity Россия  
Дата: 06.03.07 16:32
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, MouseEntity, Вы писали:


ME>>Можно использовать преобразования типов, придётся кидаться исключениями в случае если width != height, но похоже это неполное решение. А можно вообще отказаться от типизации или не разделять квадраты и прямоугольники на разные типы.


I>А если вместо квадратов и прямоугольников речь о квадратных и прямоугольных матрицах, и функция вычисления детерминанта (определителя) имеет смысл только для квадратных матриц? Определить только прямоугольную матрицу, определить функцию вычисления детерминанта для нее и "кидаться исключениями" в случае вызова этой функции для неквадратной матрицы?


Можно и исключения. Если хочется статических проверок, то лучше всего автоматическое доказательство некоторых утверждений, по сути типизация именно это и позволяет делать, только в упрощённом случае.

Если же для этого вводить отдельный тип для квадрата, то в неимперативном случае будет достаточно приведений типов — неявных от квадрата к прямоугольнику, явных или неявных от прямоугольника к квадрату (в случае явного приведения программист берёт как бы на себя доказательство утверждения). Хотя это, конечно, имитация теоретико-множественного включение: чтобы всё было математически красиво, надо использовать систему типов, в которой можно использовать типы-подмножества других типов — однако без автоматических доказательств ничего нового они имхо не дадут.
Re[11]: И все-таки квадрат это прямоугольник
От: VoidEx  
Дата: 06.03.07 18:28
Оценка:
Здравствуйте, anton_t, Вы писали:

_>А что такое "Просто Button"?

Мне что, надо приводить четкие формулировки?
Вот по Стандарту Си++, надеюсь, будет понятно, о чем я.
1.3.3 dynamic type [defns.dynamic.type]
the type of the most derived object (1.8) to which the lvalue denoted by an lvalue expression refers. [Example:
if a pointer (8.3.1) p whose static type is “pointer to class B” is pointing to an object of class D, derived
from B (clause 10), the dynamic type of the expression *p is “D.” References (8.3.2) are treated similarly. ]
The dynamic type of an rvalue expression is its static type.

Просто Button, когда dynamic type == Button.
Re: И все-таки квадрат это прямоугольник
От: Big_Urri Казахстан  
Дата: 07.03.07 03:16
Оценка:
Здравствуйте, igna, Вы писали:

I>А если захотелось добавить методы SetWidth и SetHeight, так это уже нечто за пределами математических понятий "квадрат" и "прямоугольник". В математике объекты (не в смысле ООП объекты, нет, в обычном, человеческом смысле объекты), так вот, в математике объекты как правило неизменные, то есть умножая одну матрицу на другую получаем третью, а вовсе не модифицируем первую. В программировании такие объекты тоже встречаются, например String в Java или .NET; и если Rectangle и Square определить неизменными, они вполне могут быть связаны отношением наследования.


А еще, как квадрат, так и прямоугольник можно задавать длиной диагонали и углом между ними. В этом случае проблем с наследованием и изменяемостью не возникает, поведение фигур полностью идентично. Height и Width будут вычисляемые. Но есть еще ромб.
Re[2]: И все-таки квадрат это прямоугольник
От: igna Россия  
Дата: 07.03.07 04:10
Оценка: +1
Здравствуйте, Big_Urri, Вы писали:

B_U>А еще, как квадрат, так и прямоугольник можно задавать длиной диагонали и углом между ними. В этом случае проблем с наследованием и изменяемостью не возникает, поведение фигур полностью идентично. Height и Width будут вычисляемые. Но есть еще ромб.


Хм. Угол между диагоналями квадрата всегда 90°, чего в общем случае не скажешь об угле между диагоналями прямоугольника.
Re[3]: И все-таки квадрат это прямоугольник
От: Big_Urri Казахстан  
Дата: 07.03.07 04:17
Оценка:
Здравствуйте, igna, Вы писали:

I>Хм. Угол между диагоналями квадрата всегда 90°, чего в общем случае не скажешь об угле между диагоналями прямоугольника.


Несомненно.
Но в случае, если хранить "внутре" длину диагонали и угол — для этих внутренних полей как базового класса (прямоугольник), так и для потомка (квадрат) исключено противоречивое состояние, как в случае, если хранить длину и ширину (Height<>Width для квадрата). А извне можно манипулировать длиной и шириной (как задавать, так и получать).
А основа признака квадрата — значение угла 90, но не равенство длины и ширины (это следствие прямого угла между диагоналями).
Re[4]: И все-таки квадрат это прямоугольник
От: igna Россия  
Дата: 07.03.07 04:30
Оценка:
Здравствуйте, Big_Urri, Вы писали:

B_U>Но в случае, если хранить "внутре" длину диагонали и угол — для этих внутренних полей как базового класса (прямоугольник), так и для потомка (квадрат) исключено противоречивое состояние, как в случае, если хранить длину и ширину (Height<>Width для квадрата). А извне можно манипулировать длиной и шириной (как задавать, так и получать).


Объект потомка (квадрат) в программе может рассматриваться как объект базового класса (прямоугольник), то есть программа может попытаться к примеру удвоить его ширину оставив высоту неизменной. Ну это в общем тот же аргумент, что и приводят обычно обосновывая некорректность наследования изменяемого квадрата от изменяемого прямоугольника.
Re[10]: И все-таки квадрат это прямоугольник
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.03.07 05:19
Оценка:
Здравствуйте, MouseEntity, Вы писали:
ME>Если же для этого вводить отдельный тип для квадрата, то в неимперативном случае будет достаточно приведений типов — неявных от квадрата к прямоугольнику, явных или неявных от прямоугольника к квадрату (в случае явного приведения программист берёт как бы на себя доказательство утверждения). Хотя это, конечно, имитация теоретико-множественного включение: чтобы всё было математически красиво, надо использовать систему типов, в которой можно использовать типы-подмножества других типов — однако без автоматических доказательств ничего нового они имхо не дадут.
Что-то мне подсказывает, что ситуация с квадратами/прямоугольниками банально слабо описывается тем полиморфизмом, который принят в ООП.
С точки зрения ADT, тип — это как множество значений, так и набор операций над ними. Квадратная матрица является частным случаем прямоугольной. Операция "вычисление детерминанта" применима только к ним. Выразить это традиционным способом явной типизации затруднительно. В С++ это в принципе возможно, но не благодаря ООП.
Две идеи:
1. Отнаследовать класс SquareMatrix<int N> от Matrix<N, N> и добавить в него метод Determinant(), а также оператор (неявного) преобразования из Matrix<N,N>.
Тогда можно будет писать примерно так:
Matrix<4, 3> m1;
Matrix<3, 4> m2;
double D = ((SquareMatrix<4>)(m1 * m2)).Determinant();

2. Сделать Determinant() внешней функцией, определенной только на Matrix<N, N> и писать:
Matrix<4, 3> m1;
Matrix<3, 4> m2;
double D = Determinant(m1 * m2);
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: И все-таки квадрат это прямоугольник
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.03.07 05:20
Оценка: +1
Здравствуйте, igna, Вы писали:

I>Зато квадратная матрица с одной стороны ограничивая прямоугольную равенством количества строк и столбцов, с другой стороны добавляет возможность вычисления детерминанта. Можно наследовать? То есть, что на самом деле важно, расширение или "не сужение"?


Наследовать нельзя. Потому, что собственно прямоугольная матрица с равным количеством строк и столбцов уже должна позволять вычислять детерминант. Нет никакой гарантии что мы сразу создадим объект как именно квадратную матрицу. Результат умножения двух прямоугольных матриц — в общем случае, прямоугольная матрица. И только иногда она может оказаться квадратной, и сразу научиться вычислению детерминанта. Классическое ООП в этом случае считает, что размерность матрицы является частью ее состояния, и результат операции "вычисление детерминанта" существенным образом от этого состояния зависит. В том числе этот результат может быть и выбросом исключения. В классическом ООП нет никакого способа выразить отношения типов, нужные для статического моделирования матриц с разными размерностями.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[5]: И все-таки квадрат это прямоугольник
От: Big_Urri Казахстан  
Дата: 07.03.07 06:01
Оценка:
Здравствуйте, igna, Вы писали:

I>Объект потомка (квадрат) в программе может рассматриваться как объект базового класса (прямоугольник), то есть программа может попытаться к примеру удвоить его ширину оставив высоту неизменной. Ну это в общем тот же аргумент, что и приводят обычно обосновывая некорректность наследования изменяемого квадрата от изменяемого прямоугольника.


А, ну да. Значит таки позволять задавать только угол и диагональ. Тогда длину и ширину напрямую базовый класс задавать не будет, они будут вычисляемыми. Противоречивость устраняется, но понятно, что на практике манипулировать углом и диагональю для задания прямоугольника скучно.
Re[6]: И все-таки квадрат это прямоугольник
От: igna Россия  
Дата: 07.03.07 06:08
Оценка:
Здравствуйте, Big_Urri, Вы писали:

B_U>А, ну да. Значит таки позволять задавать только угол и диагональ. Тогда длину и ширину напрямую базовый класс задавать не будет, они будут вычисляемыми. Противоречивость устраняется, но понятно, что на практике манипулировать углом и диагональю для задания прямоугольника скучно.


Думаешь так? Тогда смотри:

Объект потомка (квадрат) в программе может рассматриваться как объект базового класса (прямоугольник), то есть программа может попытаться к примеру поманипулировать углом установив его не равным 90°.
Re[7]: И все-таки квадрат это прямоугольник
От: Big_Urri Казахстан  
Дата: 07.03.07 06:25
Оценка:
Здравствуйте, igna, Вы писали:

I>Объект потомка (квадрат) в программе может рассматриваться как объект базового класса (прямоугольник), то есть программа может попытаться к примеру поманипулировать углом установив его не равным 90°.


Согласный
То же самое, но только в профиль
Re[7]: И все-таки квадрат это прямоугольник
От: Tilir Россия http://tilir.livejournal.com
Дата: 07.03.07 06:37
Оценка: 2 (2)
Здравствуйте, igna, Вы писали:

I>То есть "Прямоугольник" не может быть подтипом "Квадрата".


Немножко подумав, вынужден признать, что вы правы. Тем не менее, ЛСП наоборот также не выполняется, если мы предположим стороны изменяемыми раздельно. Для того, чтобы в вашем исходном коде не было ничего криминального, я бы переписал его вот так (не знаю, как это будет на Java). В таком случае квадрат будет поддерживать инвариант одним фактом его создания. Вводить новые поля в квадрате не нужно. В этом случае я согласен и с исходной мыслью — такой квадрат действительно является таким прямоугольником. Но модификатор const на protected членах всё-таки критичен. Это реализует вашу идею, что изменённый прямоугольник — уже другой прямоугольник. Но и в этом случае остаются проблемы (см. комментарии).

class Rectangle{
public:
  explicit Rectangle(double x, double y):m_x(x), m_y(y){};
  virtual double GetSquare() const{ return m_x*m_y;}; // Пока всё хорошо
  virtual double GetPerimeter() const{ return 2.0*(m_x+m_y);}; // Тоже ok.
  // Получаем копию масштабированную в n раз только по x
  virtual const Rectangle GetCopyX(double n){
    return Rectangle(m_x*n, m_y);
  } // Ага! доигрались.
protected:
  // Раздельная модификация (и вообще модификация) сторон невозможна.
  const double m_x;
  const double m_y;
};

// Теперь квадрат. У него переопределяется только конструктор
class Square: public Rectangle{
public:
  explicit Square(double x): ::Rectangle(x, x){};
  // Здесь придётся идти на кривые уловки, например делать
  // private: const Rectangle GetCopyX(double n){}; // чтобы её не дай Б-же не вызвали
};
Re[8]: И все-таки квадрат это прямоугольник
От: igna Россия  
Дата: 07.03.07 07:34
Оценка:
Здравствуйте, Tilir, Вы писали:

T>... Но модификатор const на protected членах всё-таки критичен.


+1

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

На Java это модификатор final:

    . . .
    final int width, height;
    . . .
Re[2]: И все-таки квадрат это прямоугольник
От: Pavel Dvorkin Россия  
Дата: 07.03.07 07:47
Оценка: +1 :))) :))
Здравствуйте, Big_Urri, Вы писали:

B_U>А еще, как квадрат, так и прямоугольник можно задавать длиной диагонали и углом между ними. В этом случае проблем с наследованием и изменяемостью не возникает, поведение фигур полностью идентично. Height и Width будут вычисляемые. Но есть еще ромб.


Только не ромб! Не надо ромбов! Никаких ромбов не надо! Дискуссию с участием еще и ромба сервер не выдержит!

P.S. Как много у программистов свободного времени
With best regards
Pavel Dvorkin
Re[3]: И все-таки квадрат это прямоугольник
От: igna Россия  
Дата: 07.03.07 08:22
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Только не ромб! Не надо ромбов! Никаких ромбов не надо! Дискуссию с участием еще и ромба сервер не выдержит!


Здесь похоже можно и ромб от квадрата произвести. А потом и четырехугольник от него.

PD> P.S. Как много у программистов свободного времени


У меня оно кончается сегодня, завтра будет так:
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.