Ciao Paolo,
 anche se l'incontro non è disponibile puoi reperire la registrazione 
dell'evento al JUG Milano qui
 

 http://www.jugmilano.it/meeting-98.html http://www.jugmilano.it/meeting-98.html
 

 Come promesso pubblico anche una possibile soluzione all'esercizio.
 Ringrazio ancora Matteo Mortari che ha ingegnosamente risolto l'esercizio in 
Java, la soluzione è allegata e se non dovesse essere chiara proverò a 
delucidarvi.
 

 Ho approfittato inoltre per cogliere il suggerimento di Simone riscrivendo la 
soluzione, togliendo quasi tutti gli operatori reattivi ed utilizzando 
preferibilmente le coroutine.
 

 Il secondo esercizio è:
 

     fun countActive(): Flux<Sensor> = GlobalScope.flux {
        controlUnit.getSensors().consumeEach { sensor ->
            launch {
                val value = iotClient.query(sensor).awaitFirst()
                if (value.isFinite()) send(sensor)
            }

 

 "flux", come detto, è un generatore di flussi reattivi, per far emettere un 
sensore al flusso bisogna utilizzare il metodo "send".
 "consumeEach" serve per iterare sullo stream reattivo.
 "launch" fa partire una coroutine, è praticamente l'analogo di "new Thread".
 "awaitFirst" sospende la coroutine aspettando il primo valore emesso dal 
sensore.
 

 La soluzione al terzo esercizio è:
 

     fun checkSensor() = GlobalScope.flux {
        val configurationDeferred = async { 
database.loadConfiguration().collectList().awaitFirst() }
        controlUnit.getSensors().consumeEach { sensor ->
            launch {
                val average = iotClient.query(sensor)
                        .take(3)
                        .collectList().awaitFirst()
                        .filter { it.isFinite() }
                        .takeIf { it.isNotEmpty() }
                        ?.average()

                val configuration =
                        checkNotNull(configurationDeferred.await().find { 
it.sensorType == sensor.type && it.context == sensor.context })
                if (average != null && average !in configuration.validRange) {
                    send(AbstractMap.SimpleEntry(sensor, average)) // sent to 
Flux
                }
            }
        }
    }
 

 "async" lancia una coroutine e ne cattura il risultato in un "Deferred" (una 
sorta di CompletableFuture).
"takeIf" è il famoso operatore magico, ritorna l'offetto dse la condizione è 
vera altrimenti torna "null" "?.average()" calcola la media, il simbolo "?." 
invece del semplice "." è il safe operator, cioè calcola la media della lista 
solo se la lista non è null, altrimenti ritorna direttamente null. in Kotlin 
non è possibile chiamare un metodo su un oggetto null, è un errore di 
compilazione (in Java è un errore a runtime).
 "await" sul configurationDeferred attande il risultato del deferred, tipo 
"future.get()"
 "find" cerca un elemento nella lista, oppure ritorna null
 "checkNotNull" verifica che il parametro non sia null e lo ritorna, altrimenti 
lancia un errore
 "average !in validRange" è compilato esattamente come il codice Java 
"!validRange.contains(average)"
 

 Se approfondite le soluzioni proposte nei due linguaggi risulta più evidente 
che gli stream reattivi hanno molti operatori solo per sopperire a deficienze 
del linguaggio, nel codice Kotlin si riesce a fare quasi tutto con async/await. 
Similmente è possibile estendere il discorso per il CompletableFuture e gli 
Optional.
 

 Grazie per la bella serata di ieri, arrivederci alla prossima.

 Vasco

Reply via email to