为了账号安全,请及时绑定邮箱和手机立即绑定

在 Java 解释器中抽象数学运算

在 Java 解释器中抽象数学运算

杨__羊羊 2023-06-14 10:39:13
我正在用 Java 编写一个 AST 解释器,它有很多方法可以检查参数类型并在它们匹配时执行操作。到目前为止,已经有五种以上的方法,它们基本上是相互复制粘贴的版本。有没有办法抽象要检查的类型和要执行的操作?    @Override    public Object visitMultiplyNode(MultiplyNode multiplyNode) {        Object lhs = multiplyNode.getLeftHandSide().accept(this);        Object rhs = multiplyNode.getRightHandSide().accept(this);        if (lhs instanceof Double && rhs instanceof Double) {            return (double) lhs * (double) rhs;        }        if (lhs instanceof Long && rhs instanceof Long) {            return (long) lhs * (long) rhs;        }        throw new TypeError("Can not multiply " + lhs.getClass() + " and " + rhs.getClass() + ".");    }我要检查的类型并不总是相同的,例如,模数节点只接受 Longs 而加法节点也接受 Strings 进行连接。    @Override    public Object visitAddNode(AddNode addNode) {        Object lhs = addNode.getLeftHandSide().accept(this);        Object rhs = addNode.getRightHandSide().accept(this);        if (lhs instanceof Double && rhs instanceof Double) {            return (double) lhs + (double) rhs;        }        if (lhs instanceof Long && rhs instanceof  Long) {            return (long) lhs + (long) rhs;        }        if (lhs instanceof String && rhs instanceof String) {            return "" + lhs + lhs;        }        throw new TypeError("Can not add " + lhs.getClass() + " and " + rhs.getClass() + ".");    }    @Override    public Object visitModulusNode(ModulusNode modulusNode) {        Object lhs = modulusNode.getLeftHandSide().accept(this);        Object rhs = modulusNode.getRightHandSide().accept(this);        if (lhs instanceof Long && rhs instanceof Long) {            return (long) lhs % (long) rhs;        }        throw new TypeError("Can not take modulus of " + lhs.getClass() + " and " + rhs.getClass() + ".");    }
查看完整描述

2 回答

?
慕运维8079593

TA贡献1876条经验 获得超5个赞

你可以使用 lambda:


private Object visitBinaryOperatorNode(BinaryOpNode node, BiFunction<T, T> op) {

    Object lhs = node.getLeftHandSide().accept(this);

    Object rhs = node.getRightHandSide().accept(this);


    if (lhs instanceof Long && rhs instanceof Long) {

        return op.apply((long) lhs, (long) rhs);

    }

    throw new TypeError("Can not take " + node.getOpName() + "of " + lhs.getClass() + " and " + rhs.getClass() + ".");

}

但是,由于您的某些运算符支持多种类型,因此您需要另一层抽象:


@RequiredArgsConstructor// Lombok, otherwise write the boilerplate yourself

public class BinaryOperator<T, T> {

   @NonNull private final BiFunction<T, T> op;

   @NonNull private final Class<T> clazz;


   public boolean isApplicable(Object left, Object right) {

       return clazz.isInstance(left) && clazz.isInstance(right);

   }


   public T apply(Object left, Object right) {

       return op.apply(clazz.cast(left), clazz.cast(right));

   }

}

您现在可以传递一组有效的二元运算符并测试它们是否适用,如果适用,则应用它们。


private static final List<BinaryOperator<?, ?>> VALID_ADD_OPERATORS = Arrays.asList(

    new BinaryOperator<>((x, y) -> x + y, Double.class), 

    new BinaryOperator<>((x, y) -> x + y, Long.class),

    new BinaryOperator<>((x, y) -> x + y, String.class)

);


private static final List<BinaryOperator<?, ?>> VALID_MULTIPLY_OPERATORS = Arrays.asList(

    new BinaryOperator<>((x, y) -> x * y, Double.class), 

    new BinaryOperator<>((x, y) -> x * y, Long.class)

);


@Override

public Object visitAddNode(AddNode addNode) {

    return visitBinaryOperatorNode(addNode, VALID_ADD_OPERATORS );

}


@Override

public Object visitMultiplyNode(MultiplyNode multiplyNode) { 

    return visitBinaryOperatorNode(multiplyNode, VALID_MULTIPLY_OPERATORS ); 

}


private Object visitBinaryOperatorNode(BinaryOpNode node, List<BinaryOperator<?, ?>> validOperators) {

    Object lhs = node.getLeftHandSide().accept(this);

    Object rhs = node.getRightHandSide().accept(this);


    for (BinaryOperator<?, ?> op : validOperators) {

        if (op.isApplicable(lhs, rhs)) return op.apply(lhs, rhs);

    }

    throw new TypeError("Can not take " + node.getOpName() + "of " + lhs.getClass() + " and " + rhs.getClass() + ".");

}


查看完整回答
反对 回复 2023-06-14
?
慕的地8271018

TA贡献1796条经验 获得超4个赞

您可以将这些检查提取到单独的对象中,并在需要时重用它。

例如,通过为要处理转换的每种类型定义一个枚举值。


例如


public enum ConvertType {


    DOUBLE {

        Object apply(Object lhs, Object rhs) {

            if (lhs instanceof Double && rhs instanceof Double) {

                return (double) lhs + (double) rhs;

            }

            return null;

        }

    },


    LONG {

        Object apply(Object lhs, Object rhs) {

            if (lhs instanceof Long && rhs instanceof Long) {

                return (long) lhs + (long) rhs;

            }

            return null;

        }

    },


    STRING {

        Object apply(Object lhs, Object rhs) {

            if (lhs instanceof String && rhs instanceof String) {

                return "" + lhs + lhs;

            }

            return null;

        }

    };


    public static Object apply(Object a, Object b, ConvertType... convertTypes) {

        for (ConvertType convertType : convertTypes) {

            Object result = convertType.apply(a, b);

            if (result != null) {

                return result;

            }

        }

        throw new TypeError("Can not take modulus of " + a.getClass() + " and " + b.getClass() + ".");


    }


}

转换的入口点是静态方法:


public static Object apply(Object a, Object b, ConvertType... convertTypes)

ConvertType多亏了var-args.


例如 :


@Override

public Object visitMultiplyNode(MultiplyNode multiplyNode) {

    Object lhs = multiplyNode.getLeftHandSide().accept(this);

    Object rhs = multiplyNode.getRightHandSide().accept(this);        

    return ConvertType.apply(lhs , rhs, ConvertType.DOUBLE, ConvertType.LONG);

}

或者 :


@Override

public Object visitAddNode(AddNode addNode) {

    Object lhs = addNode.getLeftHandSide().accept(this);

    Object rhs = addNode.getRightHandSide().accept(this);

    return ConvertType.apply(lhs , rhs, ConvertType.DOUBLE, ConvertType.LONG, ConvertType.STRING);    

}


查看完整回答
反对 回复 2023-06-14
  • 2 回答
  • 0 关注
  • 129 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信