Grazie

Il 29/03/2019 21:30, [email protected] [it-torino-java-jug] ha scritto:
[Attachment(s) <#TopText> from [email protected] [it-torino-java-jug] included below]
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

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