- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
do
{
count++;
Xt=X;
if (ii==1)
{
X= dichotomia(a,b, E);
fprintf(bnf, "%lf;\n", Xt);
}
else
{
if (ii==2)
{
X=newton(X, E);
fprintf(bnf, "%lf;\n", Xt);
}
else
{
if (ii==3)
{
X=sekuschich(X, b);
fprintf(bnf, "%lf;\n", Xt);
}
else
{
if (ii==4)
{
ii==1;
ii==2;
ii==3;
}
}
}
}
printf("X=%lf\n", X);
if(Func(X)*Func(a)>0)a=X;
else b=X;
}
while(fabs(X-Xt)>=E);
В общем лаба на численые методы поиска корней.
После того как я предоставил няшный, полностью удовлетворяющий условиям, после проверки меня попросили "сделать как у парней", и вот участок их высера
p.s. форматирование сохранить не удалось, да и там совсем бы ничего понятно бы не было
Печально. Вот так у нас и готовят будущие кадры...
и парни которые написали этот код очевидно даже не разбирались в сути методов и в тз, так как ошибок там очень много и их программа работает не правильно.
У нас тоже такая фигня была. Причем "5 курсов до тебя решали, все нормально было, а ты не можешь, иди переделывай". Но я препода тогда таки смог убедить, что косяк в задании...
Ох, как я Вас понимаю.
Так и не доказал, но зачет все же получил.
Любой метод поиска действительного корня прежде всего требует локализовать знакопеременный отрезок.
График -- один из способов локализации.
Тем не менее, в существование лабыря, который даёт задачи со знакопостоянным отрезком, на котором ещё нужно что-то локализовывать -- не верю.
Это как вампиры - вы в них не верите, но они существуют.
видели?
Только это не поиск корней уравнения был, а какой-то итерационный матан с дифурами, уже не помню.
ну и "где тут %username%, с++"?
struct II
{
int ii_;
II() {}
II(int ii): ii_(ii) {}
bool operator == (int n) const
{
if (ii_ < n)
return false;
if (n==1)
{
X= dichotomia(a,b, E);
fprintf(bnf, "%lf;\n", Xt);
}
else if (n==2)
{
X=newton(X, E);
fprintf(bnf, "%lf;\n", Xt);
}
else if (n==3)
{
X=sekuschich(X, b);
fprintf(bnf, "%lf;\n", Xt);
}
return (ii_ < 4) && (ii_ == n);
}
} ii;
std::cin >> ii.ii_;
Так лучше? :)
если это шутка, то неудачная
Я, правда, не проверял, мне лень...
Возможно, лабырь не понимал программы, которую написал студент и попросил переписать её "вот в таком китайском виде".
Если бы это была задача именно на управление ресурсами на упрощение сопровождения и т.д., то написание "правильного и симпатичного кода" стояло бы на первом месте.
А если это конкретно задача для семинаров по выч.матам, то здесь и приоритет другой. И так же как до 16 лет права не выдают, даже если всё подросток может, так же и в университете до 4го курса не будут разрешать писать "как тебе удобно", а будут требовать "как удобно семинаристу".
ты правда думаешь, что логику стоит засовывать в bool operator == (int) const?
или это гипотетическое предположение, как этот класс пристроить к вышеуказанному коду? но они несовместимы
> Если 1,2,3 - решаем одним способом. Если 4 - всеми тремя
это можно описать гораздо более элегантным способом
например, массив функций, из которого вызовется либо i-я функция, либо в цикле пробежится по всем
> как этот класс пристроить к вышеуказанному коду?
Именно!
Ясен хрен, что это всё бред и можно проще и вменяемее. Но переписывать на работе чью-то лабу по-человечески мне неинтересно. Придумать, как заставить работать извращённый говнокод, не меняя его, а только добавляя свой вместо пропущенных участков - совсем другое дело. :)
А в чём именно я несовместимость проглядел, кстати?
двойной вызов функции dichotomia
http://ideone.com/W6rNA
Мсье знает толк!
Решение нужно было засунуть в оператор double.
причем в зависимости от количества приведений к double выдавать 1..N корень:
Скажем, привидение типа указателя -- это было бы странновато, потому что все они на деле одинаковые только контракты разные и знать о контрактах может только система, которой мы заранее сообщаем.
А если это привидение объектов, то здесь может быть сколь угодно сложная логика.
Например приведение матрицы в число и числа в матрицу может быть весьма нетривиальным в зависимости о задачи.
......( 0 0 0 )
1 = ( 0 1 0 )
......( 0 0 0 )
Возникает неоднозначность - кто знает, какую туда логику автор засунул?
число в матрицу и обратно по метрике,
число в матрицу и обратно как максимальное собственное число,
число в матрицу и обратно как определитель.
Строго говоря любое отображение -- это и есть преобразование типа, а отображение может быть очень сложным. С тем же уравнением. Если задача каким-то образом требует отображения, например, полиномов в "наибольший действительный корень", то это отображение логично разместить в операторе приведения уравнения к double или float.
вместо метода с говорящим названием делать подло в операторе приведения к типу
Что, простите, тут скрывает действие? Что может быть не ясно?
if (matrix > 1.)
std::cout << "sqrt of matrix value " << (double)matrix << " is " << std::sqrt<double>(matrix) << std::endl;
ведь на форуме сказали, что с++ быстрее дельфи, опять соврали!
нет пути
> число в матрицу и обратно как максимальное собственное число,
> число в матрицу и обратно как определитель.
> Что, простите, тут скрывает действие? Что может быть не ясно?
вот и я о том же, за каким х в приведении типа скрывать какое то действие, чтобы обязательно было что то не ясно?
для собственного числа и определителя для класса матрицы обязаны быть методы eigen_value и det, а не тайные приведения к double
Что не ясно?
>обязаны быть методы eigen_value
Вовсе не обязаны. Это существенно зависит от задачи.
И вообще никто никому ничего не обязан, если на то нет хотя бы приличного императива.
Крестоблядство какое-то предлагает
Тарас, ты как нельзя прав...
Кто его знает что произойдёт?
1. В-лоб - т.е. переносим биты без изменений.
2. Округление в меньшую сторону.
3. Округление в большую сторону.
4. Округление до 0.
5. Округление до ближайшего целого.
Даже такое простое преобразование настолько многогранно, стоит ли злоупотреблять возможностью перегрузки и реализовывать в операторах приведения логику.
Всё-таки если пишете myObject.toInt() то хотите в итоге получить числовое представление числа, а не что-то другое. Если числового представления нет или оно не однозначно - не нужно делать метод toInt().
А операторы приведения типов это как раз аналоги методов toType().
Я бы так сказал:
> Неявное приведение даже double -> int - зло.
Больше скажу, наоборот - тоже.
стандарт знает, никакого округления, только отсечение дробной части
проблемы только если результат не способен хранить в себе в точности полученное обрезанное значение
4.9 Floating-integral conversions
1. An rvalue of a floating point type can be converted to an rvalue of an integer type. The conversion truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type.
Я показал насколько неоднозначна ситуация с простым приведением.
А человек тут хочет приведение из числа в матрицу сделать и обратно.
Свой стандарт будет писать?
Ну... или можешь в какую-нибудь менее интеллектуальную профессию перекинуться ;) Глядишь, всё на своих местах останется.
по моему, преобразование double -> integer очень интуитивно и даже не требует документации
а преобразование матрица - не матрица обычно крайне неинтуитивно
теперь попробуйте вышеприведенный код с матрицами понять без комментариев
Приучен с детства.
Вопрос закрыт.
Писать доки, комментарии на 3 экрана к каждой функции только из-за желания перегрузить оператор?
Глупости.
Код сам себя задокументировал: я привожу матрицу в число.
Вопрос закрыт.
Представь, что я сделаю метод OpenQuestion, который на самом деле банит всех участников темы, и напишу в документации "этот метод банит всех участников темы". Чё, нормально?
Если у тебя есть класс PriorityQueue, то это ни о чём не говорит, кроме того, что это PriorityQueue. А с какой сложностью это выполнено, как это реализовано конкретно, нужно либо в исходный код лезть, либо читать доки.
>Открываю вопрос обратно.
Open failure.
Называть вычисление детерминанта приведением к числу - это и есть заговариваться.
Я нигде не писал, что нужно располагать вычисление детерминанта в приведении к числу.
> число в матрицу и обратно как определитель.
> логично разместить в операторе приведения
http://govnokod.ru/11383#comment146305
определитель != детерминант?
Здесь ничего не сказано о том, где следует размещать логику вычисления детерминанта.
Не надо додумывать, надо читать, что написано. Не ясно -- задайте вопрос..
Если для точки (которая, как известно, вектор) определить эту перегрузку чтобы она возвращала евклидову длину вектора, то это тоже будет неправильно?
Для справки: есть и перегрузки для приведения к int и float.
Отрываю вопрос.
Закрываю вопрос.
...
Прошло время и я понял, что никакого секрета нет.
За такую логику нужно гвоздь в голову забивать. Класс матрицы/уравнения не должен содержать в себе алгоритмов решения - алгоритмов обычно много и гораздо удобней отделять их от задачи, которую они призваны решить. Это позволить легко выбирать алгоритм в рантайме, например. А приведение к типу вместо явного вызова решаюшей процедуры - грязный хак, который могут придумать разве что самоуверенные студенты "по приколу", чтобы продемонстрировать самим себе, что они могут переопределять приведение типа в таком гибком и хардкорном C++.
Не надо приписывать мне то, чего я не говорил.
Если вам требуется найти решение, то поиск решения передвигать в привидение типа действительно глупо. Но!
Если вам нужно отображение, то это и есть приведение типа, получение из A -> B.
Можно, конечно, применять "говорящие методы" .toString() .toInt() .toDouble() .toSomethingIHaveInMind().
Однако, в C++ есть действительно удобное средство для таких операций.
А вот, скажем, в Java нет. Пользуемся "говорящими методами".
>> максимальное собственное число
Нахождение этих величин - весьма нетривиальная задача, запихивать решение которой в класс матрицы - очень глупо.
> любое отображение -- это и есть преобразование типа
Отображение связано с преобразованием типа лишь косвенно. Можно легко найти примеры функций, на входе и на выходе которых один и тот же тип (id x = x).
По сути любой алгоритм является отображением (функцией), поэтому не понятно, с какой стати вы проводите различие между поиском решения и отображением.
Привидение типа -- ясный контракт получения из A в B.
Если вы хотите показать, что это контракт именно решения, а не отображения, пишите метод в алгебру.
Согласен, что привидение типа специфическое отображение из алгебры в алгебру, и потому оно и может быть сложным, и потому для него используются специальные конструкции, поясняющие, что происходит.
Нихера он не ясный. Даже способ приведения строки к числу отличается практически во всех динамических языках с нестрогой типизацией. А уж приведение матрицы к числу у меня, к примеру, вызывает когнитивный диссонанс.
Контракт прозрачен: положи А, получи B.
Положи 10р. --> получи билет.
Если вам нужно узнать как же это происходит, то это другой вопрос. Читаем документацию. И, скорее всего, подобные вещи ясны из контекста самого кода. Если вы используете библиотеку, которая делает преобразования СТО, то, почти наверняка, преобразование из числа в матрицу -- это создание диагональной матрицы по метрике Минковского.
А вот в обратную сторону неинтуитивная жопка получается.
ты это специально сказал, ведь, да?
матрицы в число через приведение типов - теперь понятно, почему пациент так отстаивал принудительное double a = static_cast<double>(b); везде, где можно
именно
Это уже конкретный конструкторский приём. А сама абстракция -- отображение. Я точно также могу сказать, что преобразование матрицы в double -- просто конструирование числа с матрицей в качестве аргумента.
>А вот в обратную сторону неинтуитивная жопка получается
Мне приходилось работать с библиотекой для газодинамики с дефлограцией, где было преобразование матрицы в double, возвращавшее оценку наибольшего собственного числа.
И это было написано в доках к библиотеке.
Когда не просто A -> B, но и какой-то медиатор нужен.
А каст -- это точно из А в B.
Если вам нужно использовать все 3 способа в одном модуле, а они помещены в оператор преобразования типа - каким хуем вы будете выбирать тот вид преобразования, который нужно провернуть в данном случае?
Ещё раз повторю.
Если вам нужно предоставить контракт именно получения детерминанта, то и стройте контракт детерминанта.
Если вы хотите предоставить контракт преобразования матрицы в число, то вы предоставляете именно этот контракт, который бесшовно выражает в С++ преобразованием типа.
>Если вам нужно использовать все 3 способа в одном модуле
Я буду использовать такую библиотеку, которая предоставляет три различных контракта.
АХАХАХ посоны прикиньте - оператор привидения! Чувак такой за пультом сидит и привидение на радиоуправлении летает!
Вызови static_cast и будет тебе явно.
Простите, но вы хуй.
Преобразование матрицы в число вовсе не мифическое, это два.
Эти 50 постов продолжаются потому, что каждый раз, когда я говорю, что тарелка красная, мне доказывают, что она круглая. Я постоянно пишу, что прекрасно понимаю, что она круглая. И, всё-таки, она красная.
Это три.
Ни одному вменяемому человеку не придет в голову "преобразовывать матрицу в число". Способов такого преобразования много. У каждого способа есть название и соответствующий контракт, которым и нужно пользоваться.
> Я буду использовать такую библиотеку, которая предоставляет три различных контракта.
Я тоже. И это будут методы, а не операторы приведения типов (т.к. 3 оператора приведения для одних и тех же типов описать невозможно).
Во-первых, придёт, и не одному.
Во-вторых, это не всегда соответствие какому-то контракту иному. Например тоже приближение максимальным собственным числом. То что, значение близко к характеристическому числу, не значит, что оно равно. Логика можно быть замысловатая, но использующая eigen_value().
Не говоря уже о том, что можно себе представить инверсию управления на этом операторе. Это, конечно, фантастика, я такого не видел. Но представить можно.
Proof or GTFO. Причем такой пруф, чтобы автор говорил именно о "преобразовании матрицы в число", а не "вычислял определитель" или "вычислял наибольшее собственное значение".
> Логика можно быть замысловатая
У этой логики есть название? Есть. Значит это уже не абстрактное "преобразование матрицы в число", а какое-нибудь "преобразование Пупкина" или "алгоритм Зихтельберга".
> Но представить можно.
Можно. А еще можно ходить по раскаленным углям и протыкать себя саблями. Но нужно ли это?
Если бы я мог дать кусочек кода и тем паче ссылку, я бы это сделал.
Здесь речь не идёт о какой-то супер-пупер open-source библиотеке.
О чём конкретно шла речь, я писал выше. Трёх реальных случаях из моей жизни.
> Но нужно ли это?
В каждом конкретном случае каждый метод делается с определённой целью и вряд ли абстрактной.
Преобразование матрицы в число, с которым я сталкивался, сделано исключительно для удобного общения одной части библиотеки с другой.
Конкретно, сделано было с целью возможности подсунуть состояние, представленное матрицей, в методы приблизительной оценки.
И удобства эксплуатации. Внутри моего куска кода, под моим именем нет неявных конструкторов. Но в случае с выхлопом "наружу", мнение окружающих важно.
Эти случаи назывались "поиск определителя", "вычисление метрики" и "вычисление наибольшего собственного значения"? Так какого ж ... вы пишете, что это абстрактное "преобразование матрицы в число". Я более чем уверен, что назывались они не toNumber(), и что в комментариях к этим методам вы писали их корректные названия, а не "Преобразование в число. Подробности в документации"...
> В каждом конкретном случае каждый метод делается с определённой целью и вряд ли абстрактной.
Полностью согласен. Но у вас, простите, по классу матрицы на каждую библиотеку? Матрица у которой оператор приведения возвращает наибольшее собственное значение, матрица, у которой оператор приведения возвращает определитель... Вот такое вот ооп...
double d = (double)(MatrixWhichCastsToDoubleAsDeter minant)M;
fxd
Ну тогда шаблоны для "приведения":
Эти случаи никак не назывались. Речь шла о том, что внутри логики преобразования одного в другое может быть запрятана более сложная логика. Включавшая в себя вычисление определителя или оценку собственного числа.
Так придумали наши американские друзья. Это сначала было несколько непривычно, но потом всё стало понятно.
Относительно преобразования числа в матрицу по метрике, это была удобная программа для работы с преобразованиями СТО.
Относительно детерминанта, что-то для порядка вычисления. Тоже для удобства навешено на оператор приведения, на случай, если логика изменится.
Привет, Perl! Там так и есть.
То есть, внутри преобразования используются иные методы, чтобы "удовлетворительно", с точки зрения поставленной задачи, преобразовать одно в другое.
Я, в проекте, с которым работаю, использую преобразование уравнения в приближение к локальному минимуму.
Ну зачем, зачем принимать решения, которые снижают понятность проекта ради копеечной выгоды... Ну неужели там тысячи поисков этого локального минимума, и писать eq.localMin() уже невмоготу...
Самое плохое тут - теряется универсальность решения. Допустим, в соседнем проекте мне надо было бы искать локальные максимумы этого уравнения - я бы добавил в этот класс еще один метод, и использовал бы эту библиотеку в двух проектах... А здесь как? Копипастить ее в другой проект и переделывать оператор? Ну не понимаю я таких "оптимизаций"...
Это вот как раз та самая идея, подсмотренная у программистов Crysler, когда я ещё учился.
Есть у меня набор уравнений и есть некая функция.
Функция принимает массив double и выплёвывает оптимальный double.
Я преобразую уравнения в double, используя некий "эмпирический" алгоритм из компонент оценки минимума. Зашарашиваю в эту супер-пупер функцию, которая выплёвывает мне оптимальный double. Нахожу несколько уравнений с близкими double, потом повторяю, пока не получится самое удачное к близкому.
Вот тот минимум и даёт мне "хорошую" характеристику системы.
Ну а почему это должно быть оператором (double) а не методом approximate()?
Это действительно мог быть и метод с "говорящим названием" .findEmpiricDoubleApproximation();
Но суть в том, чтобы получить из уравнения число, чтобы вбросить в массив. И я никак не вижу, что это улучшит прям читаемость кода. И вроде бы как люди согласны.
То есть, это на мой взгляд оно самое: абстрактное отображение. Ведь я могу туда что угодно запихнуть. Минум. Максимум. Коэффициен ряда. Главное, чтобы double был, чтобы его в массив свалить.
Я не хочу, чтобы человек, который потом будет пользоваться этим или подключать куда-нибудь, хоть что-то предполагал о том, как это происходит. А то ещё начнёт где-нибудь переиспользовать эти числа по иному назначению.
-- Ага, раз минимум, буду его пользовать. А потом в новой версии пересоберётся это дело с новым алгоритмом -- и чепец.
Прочитав название метода empiricApproximation() он поймет только то, что это каким-то образом вычисляет приближение, и не более того. Что вам и нужно. В документации к методу можно описать, что возвращаемое значение зависит от реализации, и может измениться в любой момент. Я не вижу улучшения инкапсуляции от использования оператора (double).
> И я никак не вижу, что это улучшит прям читаемость кода.
Да ну бросьте:
Неявное преобразование - трудно заметить.
Явный статик_каст - заметен, но не показывает цели этой операции, да и длиннее чем empiricApproximation().
Всё же, мне кажется, что здесь есть ещё своеобразный "психологический" момент.
Когда я пишу метод, то это больше напоминает какое-то свойство алгебры, выраженное в терминах другой.
А конверсия показывает, что тут происходит нечто более сложное.
Свойств, выраженных методами, может быть много: approximation1(), approximation2(), ... А конверсия одна.
Конверсия, вообще говоря, говорит о том, что в этом месте используется какая-то тесная связь между алгебрами, вероятно, какая-то внутренняя информация и об источнике, и о приёмнике, вообще говоря.
Я полагаю, что подобные вещи, вроде сброса состояния в число, для каких-то сторонних оценок, ближе именно к конверсии, а не к получению свойства в терминах числа.
Конверсия типа обладает, своего рода, "внутренне присущими" свойствами, которые присутствуют и на уровне проектирования, на уровне алгебр, когда кода ещё нет. По этой причине конверсия кажется прямым переходом от проекта к коду.
Много слов... И смысл при этом, действительно, размыт. Я понимаю.
Тут нужно думать о изоморфизме между отношениями на уровне моей задачи и отношениями между структурами уже в языке программирования.
А мне вот, наоборот, кажется, что конверсия должна быть простой и интуитивной.
> Я полагаю, что подобные вещи, вроде сброса состояния в число, для каких-то сторонних оценок, ближе именно к конверсии, а не к получению свойства в терминах числа.
Да нет. Это именно свойство уравнения - некая оценка, полученная на основе его изучения. Преобразованиями тут и не пахло. Все-таки математик, имхо, напишет "вычисляем меру" или "получаем эвристическую оценку", но никак не "преобразуем это уравнение в число".
Может быть это вопрос уже конкретно стиля. Я не утверждаю, что прям вот так нужно делать. Но мне кажется это более логичным, а вот вам нет.
Если бы это была "мера", то это действительно был бы метод. Тому можно много привести доводов.
А вот такой "финт" уже вызывает сомнения. Фактически я использую его для "склейки" каких-то разных участков кода. То есть, это такой "проектный шов", "бесшовно переходящий в кастинг".
Я бы уже закрыл этот вопрос. Вроде бы понятно, что все всё поняли. Я хотел указать, что задачи бывают, когда нужно бывает один тип сбросить в другой, чтобы как-то пооперировать с ним в другой алгебре, и, не исключено, что даже как-то "ожидаемо" его восстановить. И в некотором случае даже преобразование в double сложного типа может казаться людям логичным.
Да. Подобная многословность, к сожалению, приводит к усложнению сопровождения. Но тут уж... Закон сохранения халявы: или применять много разных методов даже для близких ситуаций, но тогда придётся постоянно оглядываться; или вам это не нужно, но зато всё много более предсказуемо.
С этим я полностью согласен, постом ниже я как раз привел несколько примеров, в которых такая конверсия имеет смысл.
> Я бы уже закрыл этот вопрос.
Ок.
Правильная мысль...
К примеру между двумя представлениями уравнений есть изоморфизм - здесь оператор преобразования типа очень даже к месту.
К месту оно было бы, возможно, и при преобразовании вектор-столбца в вектор-строку. (Хотя тут спорно).
Идеально вписывается конверсия и в случай преобразования трехчлена в многочлен.
Но вот между уравнением, и его решением, тем более приближенным, лежит метод его решения. Что как-бы намекает нам на то, что здесь нужно использовать метод, а не преобразование типа.
Продолжение:
Допустим, я хочу указывать точность этого самого приближения... куда мне ее передать? Вот умел бы статик каст принимать параметры, и передавать их оператору...
Например:
double determinant = DeterminantCalculator( matrix );
DeterminantCalculator - конструктор. принимающий матрицу, а получаемый объект может быть преобразован в double.
С другой стороны сразу видно что это не разу не объект а простая функция.
double calculateDeterminant( Matrix m );
Что логично сделать методом класса Matrix.
А преобразование тут не причём. Его можно встроить в объект-вычислитель-определителя, но не в объект-матрица.
> логично сделать методом класса Matrix
Логично, наверное, включить в класс некую стандартную реализацию этого метода. Но от внешних "решателей" всё равно не уйти. Дело в том, что выбор хорошего алгоритма вычисления определителя в реальной жизни зависит от свойств матрицы. И тут уж либо городить для каждого типа матрицы свой класс, что не всегда возможно, либо выносить задачу вычисления наружу.
Детерминант -- свойство квадратной матрицы. Как бы мы его не вычисляли, результат зависит от матрицы, а не от метода. Для человека логично ожидать, что это будет именно свойство матрицы.
Можно инжектировать оптимальный вычислитель в матрицу при конструировании или при перехвате вызова getDeterminant().
Удобно? Несомненно!
Клиент не заботится о том, что нужно вызвать какую-то там специальную функцию на матрицу. Клиент естественным образом обращается к свойству матрицы, при этом всегда, или почти всегда, получая оптимизированные вычисления.
Но на это требуются серьёзные накладные расходы на подобную кухню менеджмента.
Закон сохранения халявы: чем удобнее, тем больше накладных расходов. Ищем приемлемый компромисс.
Иногда вычисления настолько громоздкие, что подобные накладные расходы -- капля в море.
В идеальном мире. В реальном мире для больших матриц, значения которых заданы в виде чисел с плавающей точкой, это неверно.
Тот, кто задаёт матрицу, может знать о ней больше машины (например, что она трёхдиагональная и т.п.). Некоторые довольно эффективные алгоритмы требуют симметричности матрицы, и т.п.
Я вижу следующее:
>Можно инжектировать оптимальный вычислитель в матрицу при конструировании или при перехвате вызова getDeterminant().
Если клиент знает о матрице больше, то он будет внедрять вычислитель в матрицу руками. Скажем, в процессе вычислений матрица всегда остаётся трёхдиагональной.
Если клиент не знает какого вида будет матрица в процессе вычисления, то можно подключить "умный оптимизатор", который будет выбирать подходящий метод, анализируя матрицу до передачи управления в getDeterminant().
>> Как бы мы его не вычисляли, результат зависит от матрицы, а не от метода
В реальном мире детерминант внезапно становится не F[Matrix], а F[Matrix][Algorithm], т.е. зависит ещё и от метода, которым мы его считаем, и часто лишь можем указать границы, в которых он лежит.
Инжектить вычислитель при конструировании, конечно, можно, но я бы так не стал делать. Может, пользователю, работающему с матрицей, вовсе этот детерминант не интересен. Нужнен будет - пусть сам выбереть вычислитель. Явное лучше неявного. Умный оптимизатор скорее всего не подойдёт, слишком уж дорого выйдет его работа.
В общем, если бы я проектировал API для работы с линейной алгебной, Matrix бы отвечал только за доступ к элементам и базовые операции (сложение, умножение и т.п.). Сделал бы подклассы Matrix для специализации разреженных, ленточных, симметричных, ... матриц. Поиск собственных векторов/чисел/детерминанта/... были бы внешними операциями.
Повторюсь, так бы сделал я. Вариант со специализацией алгоритмов внутри подклассов Matrix в теории тоже приемлем.
операторы можно определить как в классе, так и вне - как инлайновый бинарный оператор
т.к. нам явно понадобятся дефолтные операции, то очевидно, в базовом классе (скажем, gen) будут операторы, умеющие складывать и т.д. с другим базовым классом
для класса потомка T1, если нужно отдельно оговорить иное поведение на сложение с другим типом (в т.ч. базовым) T2, придется описать внешние бинарные операторы парой (T1, T2) и (T2, T1)
http://ideone.com/qVyn1
- одного (T1, T2) не хватит (если не будет (T2, T1), то компилятор сперва найдет (gen, T1), приведет к (gen, gen) и успокоится)
http://ideone.com/sp2TO
ну и, думаю, очевидно, что никакого полиморфизма тут работать не будет
Помню, в универе пытался моделировать на жабе базовые математические концепции, ввёл различные типы чисел (натуральные, целые, рациональные, ...) и пытался написать для них операции сложения / вычитания / умножения / деления, которые бы имели вменяемое с точки зрения математики поведение. Желательно, чтобы всё это было типобезопасно. В общем, ситуация практически аналогичная.
Так вот, вышло всё это полным провалом. Сейчас я почти уверен, что это невозможно реализовать на Java расширяемо без унылых instanceof и кастов, ибо точный тип + неизвестен в compile time. Разве что вводить собственную систему диспетчеризации по типам. Но задача в целом нетривиальная.
Это было одной из причин, почему я заинтересовался Lisp.
но инертность производителей компиляторов и несовместимость их реализаций rtti так и не позволили продавить эту функциональность в стандарт
А никто вам и не предписывает устраивать API определённым образом.
Как вы с коллегами и клиентами (людьми имеется в виду, которые ваш код будут использовать) договоритесь, так и делайте.
Вы упоминали, что нужно городить много подклассов матриц или выносить "наружу".
Я представил бесшовный переход от анализа в проект, где вычисление детерминанта по прежнему принадлежит матрице, как оно было бы в реальной жизни, а все вычислительные нюансы спрятаны в сложности структуры. Конечно, по сути из матрицы вынесен метод вычисления, но так, что пользователю этот процесс может по прежнему казаться прозрачным.
Возьмём популярный нынче OpenSource. Чтобы решить свою задачу, вы разрабатываете библиотеку, и понимаете, что она может быть полезна потенциально многим людям. С кем и что вы собираетесь обсуждать? Скорее всего, люди захотят использовать этот код совсем не тем способом, каким вы его используете, и их требования могут серьёзно отличаться от ваших.
Вам, как разработчику библиотеки, остаётся лишь детально продумать возможные варианты использования и приложить максимум усилий на выделение и ключевый несвязанных сущностей и предоставление возможности их комбинировать, желательно, лишь допустимыми способами.
Именно эти качества я и считаю наиболее важными в публичном API: наличие ярко выраженных сущностей с понятными и удобными правилами их комбинаций.
Повторюсь, против вашего подхода (наличие у матрицы свойства determinant) я особых возражений не имею, это может быть удобно в рамках некоторых задач. Но моей бы первой мыслью была "А по какому алгоритму происходит вычисление детерминанта?".
> нормальные языки
т.е. функциональные?
А о каких таких языках говорит @TarasB я не знаю.
Кто отделяет? Из известных только Haskell и отделяет, Erlang, OCaml, Lisp, Scala, ... - колбась побочные эффекты где вздумается.
Java, к примеру, поддерживает и форсирует ООП-стиль, но не является чистым ООП-языком, ибо примитивы. Тем не менее, Java называют объектно-ориентированным языком. Аналогичная ситуация с "функциональными" языками, что вызывает много недоразумений.
Не критики ради, просто решил заострить внимание на несовершенстве терминологии.
это же и есть чистый код. или я что-то упускаю?
PASCAL АНОМАЛЕН
Компилятор: а вот тут в строке 15, возможно вы имели в виду...
Программист: Какой нах ворнинг?? Скомпилируй и заткнись!
[-Werror]
Компилятор: Что за нах ты написал в 15й строке!? Заткнись и иди перепиливать.
Программист: okay...
[/W4]
Компилятор: а вот тут в строке 16, мне кажется немного подозрительным...
Программист: да всё ок, тебе показалось, закоммичу на /W3 и пойду пить пиво
Тарас: КРЕСТОБЛЯДСКИЙ КРЕСТОКОМПИЛЯТОР!!!
[В любой ситуации]
Тарас: КРЕСТОБЛЯДСКИЙ КРЕСТОКОМПИЛЯТОР!!!
[/W4]
Я: вот на этой вот строчке компилятор выводит такое вод предупреждение, мне пришлось написать вот такой костыль, чтобы его не было, а как правильно?
Крестобляди: Ты чё, дурак что ли, warn off написать не можешь?
Самый умный крестовик: Ну попробуй добавить вот такую строчку, чтобы указать компилятору, что ты в курсе и учти, что тебе придётся не делать некоторые другие вещи.
Адеры: Допиши эту прагму, тогда ты сможешь делать то, что тебе надо, но будет запрещено делать некоторые другие вещи, на которые это предупреждение косвенно намекает.
Варнинги указывают, что код возможно плохой.
Если варнинг указывает на хороший код - виноват анализатор.
Если варнинг указывает на плохой код - виноват программист.
Но самое главное, что нужно не извращаться, а писать короткий, понятный и ясный код, который прост в сопровождении. В таком коде варнингов не будет.
А все эти while (100500 операторов для "эффективности") <пусто>; - извращения.
Эффективность добавляют в конце и не за счёт пары тактов, а за счёт замены алгоритма со сложностью O( n^2 ) на O( log n ).
Это не извращения, это C-style :)
Не говори никому. Не надо.
(c) "The C Programming Language"
Гражданин Fai — любитель(средняя весовая) вбросить.
А вот внезапно закрыть срачь(тот самый случай когда троллят троллей), можно
было немного тоньше. Ну да ладно.