У меня есть дерево логических операций из логического выражения, которое мне нужно запустить в Java. Дерево будет примерно таким: И мне нужно было бы запускать его на Java итеративно, чтобы он возвращал мне логическое значение.
boolean b = AND(isNumberEven("4"), AND(isNumberOdd("7"), isNumberEven("6"))
Проблема в том, что мне нужно написать интерфейсы, так как в моей компании мы могли бы создать множество классов, таких как And, Or, isSomething, hasInArray (это может быть любое свойство, необходимое другим разработчикам). Обычно у нас есть два типа узлов: логические, которые имеют две ветви, и узлы проверки свойств, которые являются листьями дерева.
То, что я пытался сделать ниже, я думаю, что это действительно неправильно в Java, пожалуйста, помогите мне сделать это правильно.
Моя идея состояла в том, чтобы создать два интерфейса, один для логических операторов, а другой для проверки свойств. Средство проверки свойств должно оценить свое свойство на входе, в то время как логический оператор должен оценить две ветви, а затем что-то вычислить с двумя результатами (например, И или ИЛИ, нас пока не волнует оператор НЕ). . Я думал сделать эти интерфейсы
class PropertyCheckingInput {
String s; // dummy property, this object is the thing we want to verify the property on (example, if this string is an even number)
PropertyCheckingInput(...){...} // constructor for all params
}
interface PropertyChecking {
boolean fire(PropertyCheckingInput i);
}
class LogicOperatorInput<
T extends LogicOperator or PropertyChecking, // it can be one or the other
Ti extends LogicOperatorInput or PropertyCheckingInput
> {
T left;
Ti leftInput;
T right;
Ti rightInput;
// probabily the type of leftInput should act accordingly to the type of left, but I don't know how to enforce this in Java with generics
LogicOperatorInput(...){...} // constructor for all params
}
interface LogicOperator{
boolean fire(LogicOperatorInput i);
}
В этом случае, если я хочу реализовать что-то вроде И, я мог бы сделать это как
class And implements LogicOperator {
boolean fire(LogicOperatorInput i) {
i.left.fire(i.leftInput) && i.right.fire(i.rightInput);
}
And() {}
public static void main(String[] args) {
// expression: isNumberEven("4") AND isNumberOdd("7") AND isNumberEven("6")
boolean b = new And().fire(new LogicOperatorInput(
new isNumberEven(),
new PropertyCheckingInput("4"),
new And(),
new LogicOperatorInput(
new isNumberOdd(),
new PropertyCheckingInput("7"),
new isNumberEven(),
new PropertyCheckingInput("6"))
));
System.out.println(b);
}
}
Для каждой ветки я выполняю функцию fire как слева, так и справа с их вводом и забочусь только об их результате. Затем, если я хочу создать логическое выражение, я объединяю различные узлы и входные данные.
Компиляторы, конечно же, говорят мне, что это неправильно, потому что они не могут определить правильный тип функций fire().
Я привык писать на Javascript, который позволяет такие вещи, потому что он не проверяет, что вы пытаетесь сделать. Есть ли способ сделать это в Java (я пытался использовать некоторые дженерики или абстрактные классы, но это не сработало) или, что еще лучше, правильный способ решить эту проблему (где у нас есть двоичное выражение, записанное в виде двоичного дерева, и мы хотите разрешить это, вызвав класс, связанный с каждым узлом)?
Я тоже не понимаю, что вы пытаетесь сделать, поэтому вам сложно помочь. Вы говорите, что вам нужно дерево выражений, но ваш пример использования не создает и не использует дерево, он просто вызывает некоторые функции и передает логические значения, которые они возвращают, в функцию добавления, которая возвращает логическое значение. Где в нем дерево?
Что такое A, B и C в вашем примере? Я не понимаю, почему логический оператор должен принимать четыре аргумента.
Наличие Ti в качестве параметра для fire() кажется немного неудобным (нарушение инкапсуляции). Похоже, что это будет единый интерфейс (или, может быть, адаптер), который может обернуть настоящие T и Ti. Тогда And и Or могут принимать переменное количество аргументов и делегировать их обертке/адаптеру.
Прошу прощения за путаницу, я пытался переписать примеры




Возможно, вы слишком усложняете это. Если вам нужно дерево выражений, как на изображении, создайте дерево выражений. Вам не нужно различать «операторы» и «ввод».
// this represents a tree "node"
interface BooleanExpression {
// all nodes should implement this to represent how they are evaluated
boolean evaluate();
}
// a node can be any of these types
record IsNumberEven(int number) implements BooleanExpression {
@Override
public boolean evaluate() {
return number() % 2 == 0;
}
}
record IsNumberOdd(int number) implements BooleanExpression {
@Override
public boolean evaluate() {
return number() % 2 == 1;
}
}
record And(BooleanExpression left, BooleanExpression right) implements BooleanExpression {
@Override
public boolean evaluate() {
return left().evaluate() && right().evaluate();
}
}
Использование:
boolean result = new And(
new IsNumberEven(4),
new And(
new IsNumberOdd(7),
new IsNumberEven(6)
)
).evaluate();
Если вы действительно хотите отделить «входы» узлов IsNumberOdd/IsNumberEven, вы можете это сделать. Просто обобщите BooleanExpression на Expression<T>
interface Expression<T> {
T evaluate();
}
record Constant<T>(T value) implements Expression<T> {
@Override
public T evaluate() {
return value();
}
}
record IsNumberEven(Expression<Integer> number) implements Expression<Boolean> {
@Override
public Boolean evaluate() {
return number().evaluate() % 2 == 0;
}
}
record IsNumberOdd(Expression<Integer> number) implements Expression<Boolean> {
@Override
public Boolean evaluate() {
return number().evaluate() % 2 == 1;
}
}
record And(Expression<Boolean> left, Expression<Boolean> right) implements Expression<Boolean> {
@Override
public Boolean evaluate() {
return left().evaluate() && right().evaluate();
}
}
Использование:
boolean result = new And(
new IsNumberEven(new Constant<>(4)),
new And(
new IsNumberOdd(new Constant<>(7)),
new IsNumberEven(new Constant<>(6))
)
).evaluate();
Я не думал о добавлении ввода вне метода. Спасибо!
ну, ваша основная проблема в том, что вы пытаетесь сказать, что
TвLogicOperatorявляетсяLogicOperatorилиPropertyChecking. затем в вашемAnd.fire()вы пытаетесь вызватьleft.fire()с одним аргументом. однако, еслиleftявляетсяLogicOperator, то методfire()принимает 4 аргумента, а не 1. Я не могу сказать, что полностью понимаю, что вы пытаетесь сделать, но, может быть, это поможет?