java - anymatch - ¿Por qué Stream.allMatch() devuelve verdadero para una secuencia vacía?
match java 8 (4)
Mi colega y yo tuvimos un error que se debió a nuestra suposición de que una secuencia vacía que llama a
allMatch()
devolvería
false
.
if (myItems.allMatch(i -> i.isValid()) {
//do something
}
Por supuesto, es nuestra culpa asumir y no leer la documentación.
Pero lo que no entiendo es por qué el
allMatch()
predeterminado de
allMatch()
para una secuencia vacía devuelve
true
.
¿Cuál fue el razonamiento para esto?
Al igual que
anyMatch()
(que por el contrario devuelve falso), esta operación se usa de una manera imperativa que abandona la mónada y probablemente se usa en una declaración
if
.
Teniendo en cuenta esos hechos, ¿hay alguna razón por la que es
allMatch()
que
allMatch()
true
en una secuencia vacía para la mayoría de los usos?
Aquí hay otra forma de pensar sobre esto:
allMatch()
es a
&&
qué
sum()
es a
+
Considere las siguientes declaraciones lógicas:
IntStream.of(1, 2).sum() + 3 == IntStream.of(1, 2, 3).sum()
IntStream.of(1).sum() + 2 == IntStream.of(1, 2).sum()
Esto tiene sentido porque
sum()
es solo una generalización de
+
.
Sin embargo, ¿qué sucede cuando eliminas un elemento más?
IntStream.of().sum() + 1 == IntStream.of(1).sum()
Podemos ver que tiene sentido definir
IntStream.of().sum()
, o la suma de una secuencia vacía de números, de una manera particular.
Esto nos da el "elemento de identidad" de la suma, o el valor que, cuando se agrega a algo, no tiene ningún efecto (
0
).
Podemos aplicar la misma lógica al álgebra
Boolean
.
Stream.of(true, true).allMatch(it -> it) == Stream.of(true).allMatch(it -> it) && true
Más genéricamente:
stream.concat(Stream.of(thing)).allMatch(it -> it) == stream.allMatch(it -> it) && thing
Si
stream = Stream.of()
, esta regla aún debe aplicarse.
Podemos usar el "elemento de identidad" de && para resolver esto.
true && thing == thing
, entonces
Stream.of().allMatch(it -> it) == true
.
Cuando llamo
list.allMatch
(o sus análogos en otros idiomas), quiero detectar si alguno de los elementos de la
list
no coincide con el predicado.
Si no hay elementos, ninguno podría fallar.
Mi siguiente lógica elegiría elementos y esperaría que coincidieran con el predicado.
Para una lista vacía, no elegiré ningún elemento y la lógica seguirá siendo sólida.
¿Qué pasa si
allMatch
devuelve
false
para una lista vacía?
Mi lógica directa fallaría:
if (!myList.allMatch(predicate)) {
throw new InvalidDataException("Some of the items failed to match!");
}
for (Item item : myList) { ... }
!myList.empty() && !myList.allMatch()
recordar reemplazar el cheque con
!myList.empty() && !myList.allMatch()
.
En resumen,
allMatch
devuelve
true
para una lista vacía no solo es lógicamente sólido, sino que también se encuentra en el camino feliz de la ejecución, que requiere menos comprobaciones.
Esto se conoce como verdad vacía . Todos los miembros de una colección vacía satisfacen su condición; después de todo, ¿puedes señalar uno que no lo haga?
Del mismo modo,
anyMatch
devuelve
false
, porque no puede encontrar un elemento de su colección que coincida con la condición.
Esto es confuso para muchas personas, pero resulta ser la forma más útil y consistente de definir "cualquiera" y "todos" para conjuntos vacíos.
Parece que la base de esto es la inducción matemática. Para la informática, una aplicación de esto podría ser un caso base de un algoritmo recursivo.
Si la corriente está vacía, se dice que la cuantificación se satisface por vacío y siempre es cierta. Documentos de Oracle: transmisión de operaciones y canalizaciones
La clave aquí es que está "satisfecho por vacío", lo que, por naturaleza, es algo engañoso. Wikipedia tiene una discusión decente al respecto.
En matemática pura, las declaraciones verdaderas vacías generalmente no son de interés en sí mismas, pero con frecuencia surgen como el caso base de las pruebas por inducción matemática. Wikipedia: La verdad vacía