如何在Java 8中创建一个随意的一次性lambda表达式?

时间:2016-08-29 13:58:52

标签: java lambda functional-programming java-8 dry

是否有更好的功能接口类型来保存此lambda表达式?有没有一种干净的方法来用流替换循环?寻找使用更具表现力的代码来实现此目的的方法。

问题是分析2D网格(网格)并将结果保存在一个简单的值保持类(结果)中。必须从给定点(行,列)开始沿着网格向上,向下,向左和向右走。要走的四个循环中的每一个都包含完全相同的代码,明显违反DRY。

我的解决方案是编写四个循环并在每个循环中调用一个方法。这是一个一次性的方法,所以我使用Java 8 lambda访问网格&结果结构,因为它们在词汇范围内。

Predicate<int[]> countAndBreak = (int[] coord) -> {
    int r = coord[0];
    int c = coord[1];
    if (grid[r][c] == Grid.YUMMYCANDY)
        results.count++;
    if (grid[r][c] == Grid.WALL)
        return true;
    else
        return false;
};

for (int r = row; r < grid.length; r++) {
    if (countAndBreak.test(new int[] { r, col }))
        break;
}
for (int r = row; r >= 0; r--) {
    if (countAndBreak.test(new int[] { r, col }))
        break;
}
for (int c = col; c < grid[0].length; c++) {
    if (countAndBreak.test(new int[] { row, c }))
        break;
}
for (int c = col; c >= 0; c--) {
    if (countAndBreak.test(new int[] { row, c }))
        break;
}

1 个答案:

答案 0 :(得分:3)

好吧,除非你创建自己的功能界面,否则你将无法获得更好的匹配类型。但在这种情况下,更大的障碍是使用int[]而不是专用点类型。保持这些类型,你可以得到的最好的是

Predicate<int[]> atWall  = coord -> grid[coord[0]][coord[1]] == Grid.WALL;
Predicate<int[]> isCandy = coord -> grid[coord[0]][coord[1]] == Grid.YUMMYCANDY;
int[] curr={ row, col};
results.count += countUntil(curr, p -> new int[]{ p[0]+1, p[1] }, atWall, isCandy);
results.count += countUntil(curr, p -> new int[]{ p[0]-1, p[1] }, atWall, isCandy);
results.count += countUntil(curr, p -> new int[]{ p[0], p[1]+1 }, atWall, isCandy);
results.count += countUntil(curr, p -> new int[]{ p[0], p[1]-1 }, atWall, isCandy);

…

static <T> int countUntil(
    T start, UnaryOperator<T> iterate, Predicate<T> until, Predicate<T> countable) {

    int[] holder={ 0 };
    Stream.iterate(start, iterate).anyMatch(element -> {
        if(until.test(element)) return true;
        if(countable.test(element)) holder[0]++;
        return false;
    });
    return holder[0];
}

抽象出最有缺陷的部分,即当您决定用专用坐标类型替换int[]时,您不必更改countUntil方法。另一方面,countUntil方法隐藏了可变状态的讨厌使用,当切换到Java 9时可以用干净的解决方案替换,而不需要更改调用者:

static <T> int countUntil(
    T start, UnaryOperator<T> iterate, Predicate<T> until, Predicate<T> countable) {

    return (int)Stream.iterate(start, until.negate(), iterate).filter(countable).count();
}

如果您认为最有趣的方面是如何从一个位置迭代到下一个位置的抽象,您可以找到其他可能的解决方案,这允许通过使用相同代码处理所有四个移动来消除代码重复。即使使用普通循环的方法也可以从中受益。