Mi programa tiene esta línea:

Function<String, Integer> f = (String s) -> s.chars().reduce(0, (a, b) -> 2 * a + b);

La función que se pasa para reducir no es asociativa. La documentación de Reduce dice que la función pasada debe ser asociativa.

¿Cómo puedo reescribir esto como una expresión que no rompa el contrato de reduce?

8
Nick ODell 13 nov. 2017 a las 20:23

2 respuestas

La mejor respuesta

Con la implementación actual y SI no va a utilizar paralelo , está seguro con lo que tiene ahora. Obviamente, si está de acuerdo con estas exenciones de responsabilidad.

O obviamente puede crear la función con un bucle for:

 Function<String, Integer> f = s -> {
        int first = s.charAt(0) * 2 + s.charAt(1);
        int total = first;

        for (int x = 1; x < s.length() - 1; x++) {
            total = total * 2 + s.charAt(x + 1);
        }

        return total;

    };
6
Eugene 13 nov. 2017 a las 18:00

Puede convertir esta función en una función asociativa, como se explica en esta respuesta en el ejemplo de List.hashCode(). La diferencia radica solo en el factor (2 vs. 31) y el valor inicial (1 vs. 0).

Se puede adaptar a su tarea, lo cual es especialmente fácil cuando tiene una entrada de acceso aleatorio como String:

Function<String, Integer> f =
    s -> IntStream.range(0, s.length()).map(i -> s.charAt(i)<<(s.length()-i-1)).sum();

Esto incluso se ejecutaría en paralelo, pero es poco probable que alguna vez se encuentre con cadenas tan enormes que una evaluación paralela proporcione un beneficio. Entonces, lo que queda es que la mayoría de la gente podría considerar esta solución menos legible que un simple bucle for ...


Tenga en cuenta que la solución anterior muestra un comportamiento de desbordamiento diferente, es decir, si el String tiene más de 32 char s, debido al uso del operador de turno en lugar de multiplicarlo por dos.
La solución para este problema hace que la solución sea aún más eficiente:

Function<String, Integer> f = s ->
    IntStream.range(Math.max(0, s.length()-32), s.length())
             .map(i -> s.charAt(i)<<(s.length()-i-1)).sum();

Si la cadena tiene más de 32 char s, solo procesa los últimos 32 char s, lo cual ya es suficiente para calcular el mismo resultado que su función original.

3
Holger 14 nov. 2017 a las 13:12