Мы создаем индекс вместе с условием, является ли он duplicated
i1 <- with(fitness_calculations, individual == "nnn/nn/nn/nn" & gen_check == 2)
i2 <- !duplicated(i1) & i1
fitness_calculations$gen_check[i2] <- 3
fitness_calculations
# individual gen_check acc loss
#1 nnn/nn/nn/nn 3 0.9889 0.0112
#2 nnn/n/nn 2 0.7845 0.3451
#3 nnn/nn/nn/nn 2 0.5640 0.4231
Или другой вариант - заключить в which
и извлечь только первый индекс
fitness_calculations <- structure(list(individual = c("nnn/nn/nn/nn",
"nnn/n/nn", "nnn/nn/nn/nn"
), gen_check = c(2L, 2L, 2L), acc = c(0.9889, 0.7845, 0.564),
loss = c(0.0112, 0.3451, 0.4231)), class = "data.frame", row.names = c("1",
"2", "3"))
Это зависит от предполагаемой семантики A, B и C и семантики compare (). Сравнение - это абстрактное понятие, которое не обязательно имеет единственное правильное значение (или вообще какое-либо значение, если на то пошло). На этот вопрос нет единственного правильного ответа.
Вот два сценария, в которых сравнение означает две совершенно разные вещи с одной и той же иерархией классов:
class Object
{
virtual int compare(const Object& ) = 0;
float volume;
};
class Animal : Object
{
virtual int compare(const Object& );
float age;
};
class Zebra : Animal
{
int compare(const Object& );
};
Мы можем рассмотреть (по крайней мере) два способа сравнения двух Зебр: которая старше, а какой объем больше? Оба сравнения действительны и легко вычислимы; разница в том, что мы можем использовать объем для сравнения зебры с любым другим объектом, но мы можем использовать возраст только для сравнения зебр с другими животными. Если мы хотим, чтобы compare () реализовал семантику сравнения возраста, не имеет смысла определять compare () в классе Object, поскольку семантика не определена на этом уровне иерархии. Стоит отметить, что ни один из этих сценариев не требует какого-либо преобразования, поскольку семантика определяется на уровне базового класса (будь то Object при сравнении объема или Animal при сравнении возраста).
Это поднимает более важный вопрос. проблема в том, что некоторые классы не подходят для одной универсальной функции compare (). Часто имеет смысл реализовать несколько функций, которые явно указывают, что сравнивается, например compare_age () и compare_volume (). Определение этих функций может происходить в той точке иерархии наследования, где семантика становится актуальной, и адаптировать их к дочерним классам должно быть тривиально (если это вообще необходимо).
У меня едва есть эта проблема в C++. В отличие от Java, мы не обязаны наследовать все наши классы от того же класса корневого объекта. При контакте с сопоставимым (/оценивают семантику) классов, она очень вряд ли сделает, чтобы они произошли из полиморфной иерархии.
Если потребность реальна в Вашей конкретной ситуации, Вы вернулись в к double-dispatch/multimethods проблеме. Существуют различные способы решить его (dynamic_cast, таблицы функций для возможных взаимодействий, посетителей...)
Если Вы подразумеваете, что Сравнивание () в классе B или C должно всегда передаваться объект класса B или C, независимо от того, что говорит подпись, можно работать с указателями на экземпляры вместо экземпляров и попробовать к удрученному указатель в коде метода с помощью чего-то как
int B::Compare(A *ptr)
{
other = dynamic_cast <B*> (ptr);
if(other)
... // Ok, it was a pointer to B
}
(Такая перегрузка была бы необходима только для тех производных классов, которые добавляют к состоянию их родителя что-то, что влияет на сравнение.)
Вероятно, я сделал бы это как это:
class A
{
public:
virtual int Compare (const A& rhs) const
{
// do some comparisons
}
};
class B
{
public:
virtual int Compare (const A& rhs) const
{
try
{
B& b = dynamic_cast<A&>(rhs)
if (A::Compare(b) == /* equal */)
{
// do some comparisons
}
else
return /* not equal */;
}
catch (std::bad_cast&)
{
return /* non-equal */
}
}
};
Сравнивание должно быть отражающим, таким образом:
let a = new A
let b = new B (inherits from A)
if (a.equals(b))
then b.equals(a) must be true!
Так a.equals(b)
должен возвратить false, так как B, вероятно, содержит поля, которые не имеет A, что означает b.equals(a)
вероятно, будет ложь.
Таким образом в C++ сравнение должно быть виртуальным, я предполагаю, и необходимо использовать проверку типа, чтобы видеть, что параметр имеет "тот же" тип как текущий объект.
В дополнение к dynamic_cast также необходимо передать ссылку или указатель, вероятно, константа, Сравнить функция может также, вероятно, быть константой.
class B: public A
{
B();
virtual ~B();
virtual int Compare(const A &Other) const;
};
int B::Compare(const A &Other) const
{
const B *other = dynamic_cast <const B*> (&Other);
if(other) {
// compare
}
else {
return 0;
}
}
Править: Должен скомпилировать перед регистрацией...
Я реализовал бы его как это:
class A{
int a;
public:
virtual int Compare(A *other);
};
class B : A{
int b;
public:
/*override*/ int Compare(A *other);
};
int A::Compare(A *other){
if(!other)
return 1; /* let's just say that non-null > null */
if(a > other->a)
return 1;
if(a < other->a)
return -1;
return 0;
}
int B::Compare(A *other){
int cmp = A::Compare(other);
if(cmp)
return cmp;
B *b_other = dynamic_cast<B*>(other);
if(!b_other)
throw "Must be a B object";
if(b > b_other->b)
return 1;
if(b < b_other->b)
return -1;
return 0;
}
Это очень похоже на IComparable
шаблон в.NET, которая работает очень хорошо.
Править:
Один протест к вышеупомянутому - это a.Compare(b)
(где a
A и b
B), может возвратить равенство и никогда не будет выдавать исключение, тогда как b.Compare(a)
будет. Иногда это - то, что Вы хотите, и иногда это не. Если это не, то Вы, вероятно, не хотите Ваш Compare
функция, чтобы быть виртуальными, или Вы хотите выдержать сравнение type_info
s в основе Compare
функция, как в:
int A::Compare(A *other){
if(!other)
return 1; /* let's just say that non-null > null */
if(typeid(this) != typeid(other))
throw "Must be the same type";
if(a > other->a)
return 1;
if(a < other->a)
return -1;
return 0;
}
Обратите внимание что производные классы Compare
функции не должны изменяться, так как они должны назвать базовый класс Compare
, где type_info
сравнение произойдет. Можно, однако, заменить dynamic_cast
в переопределенном Compare
функция с a static_cast
.
Я предложил бы не сделать это виртуальным. Единственный недостаток - то, что явный необходимо сказать, которые выдерживают сравнение с использованием, если классы не являются тем же. Но потому что Вы имеете к, Вы могли бы определить ошибку (во время компиляции), которая иначе могла вызвать ошибку периода выполнения...
class A
{
public:
A(){};
int Compare(A const & Other) {cout << "A::Compare()" << endl; return 0;};
};
class B: public A
{
public:
B(){};
int Compare(B const & Other) {cout << "B::Compare()" << endl; return 0;};
};
class C: public A
{
public:
C(){};
int Compare(C const & Other) {cout << "C::Compare()" << endl; return 0;};
};
int main(int argc, char* argv[])
{
A a1;
B b1, b2;
C c1;
a1.Compare(b1); // A::Compare()
b1.A::Compare(a1); // A::Compare()
b1.Compare(b2); // B::Compare()
c1.A::Compare(b1); // A::Compare()
return 0;
}