Sono piuttosto confuso con queste due funzioni fold()
e reduce()
in Kotlin qualcuno può darmi un esempio concreto che le distingua entrambe?
Sono piuttosto confuso con queste due funzioni fold()
e reduce()
in Kotlin qualcuno può darmi un esempio concreto che le distingua entrambe?
Risposte:
fold
accetta un valore iniziale e la prima invocazione della lambda che si passa ad essa riceverà quel valore iniziale e il primo elemento della raccolta come parametri.
Ad esempio, prendi il seguente codice che calcola la somma di un elenco di numeri interi:
listOf(1, 2, 3).fold(0) { sum, element -> sum + element }
La prima chiamata a lambda sarà con parametri 0
e 1
.
La possibilità di trasferire un valore iniziale è utile se è necessario fornire una sorta di valore o parametro predefinito per l'operazione. Ad esempio, se si cercava il valore massimo all'interno di un elenco, ma per qualche motivo si desidera restituire almeno 10, è possibile effettuare le seguenti operazioni:
listOf(1, 6, 4).fold(10) { max, element ->
if (element > max) element else max
}
reduce
non accetta un valore iniziale, ma inizia invece con il primo elemento della raccolta come accumulatore (chiamato sum
nell'esempio seguente).
Ad esempio, facciamo di nuovo una somma di numeri interi:
listOf(1, 2, 3).reduce { sum, element -> sum + element }
La prima chiamata al lambda qui sarà con parametri 1
e 2
.
È possibile utilizzare reduce
quando l'operazione non dipende da valori diversi da quelli della raccolta a cui la si applica.
emptyList<Int>().reduce { acc, s -> acc + s }
produrrà un'eccezione, ma emptyList<Int>().fold(0) { acc, s -> acc + s }
è OK.
listOf<Int>(1, 2).reduce { acc: Number, i: Int -> acc.toLong() + i }
(il tipo di lista è Int mentre il tipo di accumulatore viene dichiarato come Numero e in realtà è un Long)
La principale differenza funzionale che vorrei evidenziare (che è menzionata nei commenti sull'altra risposta, ma potrebbe essere difficile da capire) è che reduce
genererà un'eccezione se eseguita su una raccolta vuota.
listOf<Int>().reduce { x, y -> x + y }
// java.lang.UnsupportedOperationException: Empty collection can't be reduced.
Questo perché .reduce
non sa quale valore restituire in caso di "nessun dato".
In contrasto con ciò .fold
, che richiede di fornire un "valore iniziale", che sarà il valore predefinito in caso di una raccolta vuota:
val result = listOf<Int>().fold(0) { x, y -> x + y }
assertEquals(0, result)
Quindi, anche se non vuoi aggregare la tua raccolta fino a un singolo elemento di un tipo diverso (non correlato) (che .fold
ti consentirà solo di farlo), se la tua raccolta iniziale potrebbe essere vuota, devi controllare la tua raccolta prima le dimensioni e poi .reduce
, o semplicemente usare.fold
val collection: List<Int> = // collection of unknown size
val result1 = if (collection.isEmpty()) 0
else collection.reduce { x, y -> x + y }
val result2 = collection.fold(0) { x, y -> x + y }
assertEquals(result1, result2)