查找坐标是否为顶点/边界坐标的有效方法



想象一个笛卡尔平面,每个Cell对象都代表该平面中的一个点(该平面将是迷宫)。在构建迷宫时,我想弄清楚Cell对象是顶点(四个角点)还是只是边界点(迷宫边缘的任何细胞,顶点也是边界点)。

我需要知道,这样我就可以添加相邻的单元格作为特定单元格的邻居(我正在创建一个带有节点的图结构)。不同的边界对哪些单元是邻居有不同的要求(例如,右上角的顶点不能有一个y+1或x+1的邻居,因为它在迷宫之外,而左下角的顶点则不能有y-1或x-1)。

我是如何通过一系列if语句来实现这一点的,我觉得这不是一个很好的实践。所以我想问,是否有更好的方法来知道一个点是什么类型的坐标?

以下是我的做法:

private String typeOfBorderCell(Cell cell){
    if (!isBorderCell(cell)){
        throw new IllegalArgumentException("cell is not a border cell");
    }
    double x = cell.getCoordinate().getX();
    double y = cell.getCoordinate().getY();
    // Vertices
    if (x == 0 && y == 0){
        return "bottom-left";
    }
    else if (x == 0 && y == height - 1){
        return "top-left";
    }
    else if (x == width - 1 && y == 0){
        return "bottom-right";
    }
    else if (x == width - 1 && y == height - 1){
        return "top-right";
    }
    // Non-Vertices
    else if (x == 0 && (y > 0 && y < height - 1)){
        return "left";
    }
    // and so on for the other three non-vertex borders
}

高度/宽度是迷宫的大小,但我必须减去1,因为迷宫坐标从原点(0,0)开始,因此5x5迷宫的y和x的最大值分别为4和4。

这样做,我总共会得到8个条件语句(使用此方法的方法也需要一个包含8种情况的switch语句)。有没有一种更有效的方法可以做到这一点,而不需要一堆条件语句?

我发现enums是一个相当优雅的替代if长语句集的方法。下面是一个例子(使用Java8):

enum CellType {
    OTHER(1, (x, y) -> true),
    TOP(2, (x, y) -> y == HEIGHT - 1),
    BOTTOM(2, (x, y) -> y == 0),
    LEFT(2, (x, y) -> x == 0),
    RIGHT(2, (x, y) -> x == WIDTH - 1),
    TOP_LEFT(3, TOP, LEFT),
    BOTTOM_RIGHT(3, BOTTOM, RIGHT),
    TOP_RIGHT(3, TOP, RIGHT),
    BOTTOM_LEFT(3, BOTTOM, LEFT);
    private static final int HEIGHT = 5;
    private static final int WIDTH = 5;
    private final int precedence;
    private final BiPredicate<Integer, Integer> test;
    private CellType(int precedence, BiPredicate<Integer, Integer> test) {
        this.precedence = precedence;
        this.test = test;
    }
    private CellType(int precedence, CellType type1, CellType type2) {
        this(precedence, type1.test.and(type2.test));
    }
    public static CellType valueOf(int x, int y) {
        assert x >= 0 && x < WIDTH && y >= 0 && y < WIDTH;
        return Arrays.stream(values())
            .filter(ct -> ct.test.test(x, y))
            .max(Comparator.comparingInt(ct -> ct.precedence))
            .orElse(OTHER);
    }
}

您可以将其与类似CellType.valueOf(0, 4)的代码一起使用,后者将返回CellType.TOP_LEFT

与if语句集相比,我更喜欢这个习语,因为它将谓词放在一个位置,使它们易于识别和更改。

这也会导致你的"单元格类型"不是字符串,如果你以后想向它添加逻辑,这是一个好主意。例如,你可以通过将处理单元格类型的逻辑添加到枚举本身来避免问题中提到的switch语句。此外,与字符串进行比较非常容易出错。你可以在一个地方更改字符串,结果会出现难以检测的错误。如果更改枚举,则会立即出现语法错误。

以下是它的工作原理的简要解释。BiPredicate是一个函数接口,它接受两个int(x和y)并返回一个布尔值。每个CellType成员都有一个谓词,用于测试给定的x和y是否表示该类型的单元格。对于边缘单元格类型,使用lambda表达式来提供条件。对于顶点,构造函数采用两种边单元类型,并通过测试单元是否满足这两个边条件来构造新的谓词。例如,TOP_LEFT测试单元格是否同时位于上边缘和左边缘。

valueOf方法查找满足给定单元格的所有单元格类型,然后返回优先级最高的单元格类型。优先级可确保返回顶点而不是边。如果没有匹配的单元格类型,则返回OTHER(用于非边缘非垂直)。

最新更新