Le chiamate di coda sono ottimizzate in Frege. So che c'è il TCO nè in Java nè nelle lingue che compilano in codice bytecode JVM come Clojure e Scala. Che mi dici di Frege?Frege esegue l'ottimizzazione delle chiamate tail?
risposta
Frege do Tail Ottimizzazione della ricorsione semplicemente generando cicli while.
Le chiamate di coda generali sono gestite "a proposito" attraverso la pigrizia. Se il compilatore vede una chiamata di coda a una funzione sospetta che è nota per essere (indirettamente) ricorsiva, viene restituito un risultato pigro (un thunk). Quindi, il vero onere di chiamare quella funzione risiede nel chiamante. In questo modo, gli stack la cui profondità dipende dai dati vengono evitati.
Detto questo, già la profondità dello stack statico è per sua natura più profonda in un linguaggio funzionale rispetto a Java. Quindi, alcuni programmi dovranno ricevere uno stack più grande (cioè con -Xss1m).
Esistono casi patologici, in cui vengono generati i thunk grandi e quando vengono valutati, si verificherà un overflow dello stack. Un esempio famoso è la funzione foldl (stesso problema di Haskell). Quindi, la piega di sinistra standard in Frege è fold, che è ricorsiva e rigida nell'accumulatore e quindi funziona nello spazio di stack costante (come Haskells foldl ').
Il seguente programma dovrebbe non overflow dello stack, ma stampa "false" dopo 2 o 3 secondi:
module Test
-- inline (odd)
where
even 0 = true
even 1 = false
even n = odd (pred n)
odd n = even (pred n)
main args = println (even 123_456_789)
Questo funziona come segue: println deve avere un valore per la stampa, quindi cerca di valutare (anche n). Ma tutto ciò che ottiene è un thunk a (dispari (pred n)). Quindi cerca di valutare questo thunk, che ottiene un altro thunk (anche (pred (pred n))). anche deve valutare (pred (pred n)) per vedere se l'argomento era 0 o 1, prima di restituire un altro thunk (dispari (pred (n-2)) dove n-2 è già valutato. In questo modo, tutte le chiamate (a livello JVM) viene eseguito da println. In ogni istante nemmeno invoca lo odd, o viceversa.
Se si disimpegna la direttiva inline, si ottiene una versione ricorsiva della coda pari, e il risultato viene ottenuto dieci volte . più veloce
Inutile dire che questo algoritmo goffo è solo per dimostrazione -. normalmente si potrebbe verificare la presenza di even-ness con un'operazione po
Ecco un'altra versione, che è patologico e si impilare overflo w:
even 0 = true
even 1 = false
even n = not . odd $ n
odd = even . pred
Il problema è qui che not
è la chiamata coda ed è severo nel suo argomento (vale a dire, per negare qualcosa, è necessario innanzitutto avere quel qualcosa). Quindi, quando viene calcolato even n
, l'not
deve valutare completamente odd n
che, a sua volta, deve valutare completamente even (pred n)
e quindi richiederà 2 * n frame di stack.
Sfortunatamente, questo non cambierà, anche se la JVM dovrebbe avere una coda adeguata una volta. La ragione è la ricorsione nell'argomento di una funzione rigorosa.
Sorprendente risposta, grazie mille! A proposito ci sono annotazioni di rigore in Frege? – haskelline
Sì. Modelli Bang. – Ingo
@Landei TCO non è completamente supportato in Scala ... prova questo.
def foo() { def bar() { println("bar"); foo() }; println("foo"); bar() }
Nota, non ho abbastanza reputazione per commentare direttamente. Trova un commento a cui sto rispondendo nei commenti della domanda originale.
- 1. MATLAB esegue l'ottimizzazione delle chiamate tail?
- 2. L'array_walk_recursive utilizza l'ottimizzazione delle chiamate tail?
- 3. È possibile forzare l'ottimizzazione delle chiamate tail su GCC/Clang?
- 4. Come si esegue il debug delle macro?
- 5. Ruby esegue l'ottimizzazione chiamata coda?
- 6. Ottimizzazione delle chiamate al database
- 7. Kotlin List tail function
- 8. I compilatori di famiglie ML fanno qualche ottimizzazione sofisticata per le chiamate tail?
- 9. Go: "tail -f" generatore -come
- 10. unix redirect tail to file
- 11. Logstash log tail nel browser
- 12. asp.net mvc 4 controller esegue più chiamate ajax in parallelo
- 13. Ottieni i nomi delle funzioni dallo stack delle chiamate
- 14. Come accedere all'elenco delle chiamate Android?
- 15. limitazione delle chiamate http api con ritardo
- 16. Soppressione delle chiamate per la stampa (python)
- 17. Tracciamento delle chiamate alle proprietà implementate automaticamente
- 18. Intercettazione dinamica delle chiamate in .NET
- 19. Debug delle chiamate OutputDebugString in Delphi
- 20. Come ispezionare lo stack delle chiamate
- 21. Genera l'albero delle chiamate dal database cscope
- 22. Tracciamento delle chiamate al metodo specifico
- 23. Come pipe tail -f in awk
- 24. Come il comando iwlist() esegue la scansione delle reti wireless?
- 25. L'istruzione CLR .tail disabilita il GC preemptive?
- 26. automake: esegue automaticamente i test delle unità
- 27. Qual è la differenza tra "pacchetto" e "modulo" in Frege?
- 28. Espressione desiderosa a Frege ma pigra a Haskell?
- 29. Akka con Frege che funziona più lentamente della controparte Scala
- 30. In che modo Frege generalizza i numeri letterali?
Il titolo della tua domanda è la prima cosa che la gente vede, e "TCO" è solo un altro TLA. –
Va detto che Scala ha il TCO e che anche alcune JVM (come IBM) lo implementano. – Landei
@Landei è una novità in Scala? Il TCO non è stato supportato in Scala per molto tempo! – haskelline