So che un modo per implementarlo è ricalcolare lo stato ogni volta che arriva un cambiamento, tuttavia questo sembra poco pratico.
Se le modifiche applicate quando si verifica un evento non sono distributive, in un modo o nell'altro, dovrai ricalcolare lo stato ogni volta che si verifica un evento, poiché lo stato finale non è altro che lo stato iniziale, più le modifiche successive. E anche se le modifiche sono distributive, in genere si desidera trasformare in successione uno stato in quello successivo, poiché si desidera interrompere il processo con la stessa velocità con cui viene raggiunto un determinato stato e poiché è necessario calcolare lo stato successivo per determinare se il quello nuovo è lo stato desiderato.
Nella programmazione funzionale, i cambiamenti di stato sono generalmente rappresentati da chiamate di funzione e / o parametri di funzione.
Poiché non è possibile prevedere quando verrà calcolato lo stato finale, non è necessario utilizzare la funzione ricorsiva non di coda. Un flusso di stati, in cui ogni stato è basato su quello precedente, potrebbe essere una buona alternativa.
Quindi, nel tuo caso, risponderei alla domanda con il seguente codice, in Scala:
import scala.util.Random
val initState = 0.0
def nextState(state: Double, event: Boolean): Double = if(event) state + 0.3 else state - 0.1 // give a new state
def predicate(state: Double) = state >= 1
// random booleans as events
// nb: must be a function in order to force Random.nextBoolean to be called for each element of the stream
def events(): Stream[Boolean] = Random.nextBoolean #:: events()
val states: Stream[Double] = initState #:: states.zip(events).map({ case (s,e) => nextState(s,e)}) // a stream of all the successive states
// stop when the state is >= 1 ;
// display all the states computed before it stopped
states takeWhile(! predicate(_)) foreach println
Che può dare, ad esempio (ho semplificato l'output):
0.0
0.3
0.2
0.5
0.8
val states: Stream[Double] = ...
è la linea in cui vengono calcolati gli stati successivi.
Il primo elemento di questo flusso è lo stato iniziale del sistema. zip
unisce il flusso di stati con il flusso di eventi in un singolo flusso di coppie di elementi, ogni coppia essendo un (stato, evento). map
trasforma ogni coppia in un singolo valore come nuovo stato, calcolato in funzione del vecchio stato e dell'evento associato. Un nuovo stato è quindi uno stato precedentemente calcolato, più l'evento associato che "modifica" lo stato.
Quindi, in sostanza, definisci un flusso potenzialmente infinito di stati, ogni nuovo stato essendo una funzione dell'ultimo stato calcolato e un nuovo evento. Poiché i flussi sono pigri in Scala (tra gli altri), ce ne sono solo su richiesta, quindi non è necessario calcolare gli stati inutili e si possono calcolare tutti gli stati che si desidera.
Se sei interessato solo al primo stato che rispetta il predicato, sostituisci l'ultima riga di codice con:
states find predicate get
Che recupera:
res7: Double = 1.1