是否有可能合理地模拟yield-syntax,也许是在Java 8的帮助下?

ski*_*iwi 14 java iterator yield java-8

我今天正在尝试这个问题,来自欧拉问题:

回文数字两种方式相同.由两个2位数字的乘积制成的最大回文是9009 = 91×99.

找到由两个3位数字的乘积制成的最大回文.

我考虑过它,它当然可以用for循环完成,但是我想使用Java 8,因为它打开了新的选项.

但首先,我不知道如何生成IntStream产生这样的元素,所以我仍然使用正常的for循环:

public class Problem4 extends Problem<Integer> {
    private final int digitsCount;

    private int min;
    private int max;

    public Problem4(final int digitsCount) {
        this.digitsCount = digitsCount;
    }

    @Override
    public void run() {
        List<Integer> list = new ArrayList<>();
        min = (int)Math.pow(10, digitsCount - 1);
        max = min * 10;

        for (int i = min; i < max; i++) {
            for (int j = min; j < max; j++) {
                int sum = i * j;
                if (isPalindrome(sum)) {
                    list.add(sum);
                }
            }
        }

        result = list.stream().mapToInt(i -> i).max().getAsInt();
    }

    private boolean isPalindrome(final int number) {
        String numberString = String.valueOf(number);
        String reversed = new StringBuilder(numberString).reverse().toString();
        return (numberString.equals(reversed));
    }

    @Override
    public String getName() {
        return "Problem 4";
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我可能有点懒惰,真的IntStream::max是一个非常好的方法,我认为最好使用它,因为自己写.

这里谈到的问题,不过,我需要有一个list我们能够获得这样的最大值,这意味着我需要存储的数据,在这里我真的不应该这样做.

那么,现在的问题是,是否可以在Java 8中实现这一点?

for (int i = min; i < max; i++) {
    for (int j = min; j < max; j++) {
        yield i * j;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后从那个方法创建一个PrimitiveIterator.OfInt(unboxes版本Iterator<Integer>,或IntStream直接创建?
然后得到答案streamFromYield.filter(this::isPalindrome).max().getAsInt()将非常容易实现.

最后,我知道这个问题之前已经被问过了,但是最后一次已经很久了,现在Java 8将很快发生,他们已经添加了大概念Stream<T>和新的语言结构,称为lambdas.
因此,现在制作这样的代码可能与人们为Java 6或7制作代码时的情况大不相同.

Stu*_*rks 7

好吧,我认为我们已经从"外部"使用Streams API,使用flatMap,优化回文查找算法,等等.请参阅Boris the Spiderassylias的答案.但是,我们已经回避了如何使用Python的yield语句编写生成器函数的原始问题.(我认为OP的嵌套 - 例如yield使用Python.)

使用的一个问题flatMap是并行拆分只能在最外层流上进行.内部流(从中返回flatMap)按顺序处理.我们可以尝试使内部流也平行,但它们可能与外部流竞争.我认为嵌套拆分可以工作,但我不太自信.

一种方法是使用Stream.generate或(如assylias'答案)Stream.iterate函数.但是,这些会创建无限的流,因此limit必须提供外部来终止流.

如果我们能够创建一个有限但"平坦化"的流,那么整个值的流可能会分裂,这将是很好的.不幸的是,创建一个流并不像Python的生成器函数那么方便.不过,它可以毫不费力地完成.这是一个使用StreamSupportAbstractSpliterator类的示例:

class Generator extends Spliterators.AbstractIntSpliterator {
    final int min;
    final int max;
    int i;
    int j;

    public Generator(int min, int max) {
        super((max - min) * (max - min), 0);
        this.min = min;
        this.max = max;
        i = min;
        j = min;
    }

    public boolean tryAdvance(IntConsumer ic) {
        if (i == max) {
            return false;
        }
        ic.accept(i * j);
        j++;
        if (j == max) {
            i++;
            j = min;
        }
        return true;
    }
}

public static void main(String[] args) {
    Generator gen = new Generator(100, 1000);
    System.out.println(
        StreamSupport.intStream(gen, false)
            .filter(i -> isPalindrome(i))
            .max()
            .getAsInt());
}
Run Code Online (Sandbox Code Playgroud)

不是让迭代变量在堆栈上(如在with yield-for with yield方法中),我们必须使它们成为一个对象的字段,并tryAdvance在迭代完成之前增加它们.现在,这是分离器的最简单形式,并不一定能很好地并行化.通过额外的工作,可以实现该trySplit方法以进行更好的分割,这反过来将实现更好的并行性.

forEachRemaining方法可以被覆盖,它看起来几乎像嵌套for-loop-with-yield示例,调用IntConsumer而不是yield.遗憾的tryAdvance是它是抽象的,因此必须实现,因此仍然需要使迭代变量成为对象的字段.


ass*_*ias 1

我会尝试一下。带有循环的版本,然后带有流的版本。虽然我从另一端开始,所以更容易,因为我可以limit(1)

public class Problem0004 {

    public static void main(String[] args) {
        int maxNumber = 999 * 999;
        //with a loop
        for (int i = maxNumber; i > 0; i--) {
            if (isPalindrome(i) && has3DigitsFactors(i)) {
                System.out.println(i);
                break;
            }
        }
        //with a stream
        IntStream.iterate(maxNumber, i -> i - 1)
                .parallel()
                .filter(i -> isPalindrome(i) && has3DigitsFactors(i))
                .limit(1)
                .forEach(System.out::println);
    }

    private static boolean isPalindrome(int n) {
        StringBuilder numbers = new StringBuilder(String.valueOf(n));
        return numbers.toString().equals(numbers.reverse().toString());
    }

    private static boolean has3DigitsFactors(int n) {
        for (int i = 999; i > 0; i--) {
            if (n % i == 0 && n / i < 1000) {
                return true;
            }
        }
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)