2016-07-12 11 views
1

Ich sehe Funktionen, die einen Empfangsblock umschließen, der rekursiv scheinbar nicht in der Schwanzposition überall im elixir Beispielcode aufgerufen wird. Zum Beispiel:Tail-Rekursion in einem Empfangsblock

defmodule A do 
    def loop do 
    receive do 
     :ping -> 
     IO.puts "Received :ping" 
     loop # <- Tail position? 
     :pong -> 
     IO.puts "Received :pong" 
     loop # <- Also Tail position? 
     after 
     5000 -> 
      loop # <- Also Tail position? 
    end 
    loop # <- Also Tail position? 
    end 
end 

Erhalten ein spezielles Konstrukt, das für Endpositionen am Ende aller Match-Blöcke optimiert? Wenn ja, gilt es, wenn der Empfangsblock einen after Block hat? Was passiert, wenn nach dem Empfangsblock Code in der Funktion ist?

Antwort

5

In Ihrem Beispiel ist nur der allerletzte Aufruf von loop tail rekursiv. Anstatt den rekursiven Aufruf innerhalb des receive Blocks zu machen, lassen Sie einfach die receive zurückkehren und verwenden Sie einfach den tailrekursiven Aufruf, um am Ende der Funktion, die bereits dort ist, eine Schleife auszuführen.

defmodule A do 
    def loop do 
    receive do 
     :ping -> 
     IO.puts "Received :ping" 
     :pong -> 
     IO.puts "Received :pong" 
     after 
     5000 -> 
      nil 
    end 
    loop 
    end 
end 

Als allgemeine Regel gilt, Prozessschleifen sollten immer genau einen rekursiven Aufruf am Ende der loop Funktion. Das ist der einfachste Weg, um zu garantieren, dass der Prozess unendlich wiederholt werden kann.

aber Sie können Schleife innerhalb des receive Block, wenn es die letzte Anweisung in der loop Funktion ist. Das Ergebnis wird auch tail-rekursiv sein. Auf diese Weise können Sie bedingt die Schleife auf einer empfangenen Nachricht basiert stoppen

defmodule A do 
    def loop do 
    receive do 
     :ping -> 
     IO.puts "Received :ping" 
     loop 
     :pong -> 
     IO.puts "Received :pong" 
     loop 
     :stop -> 
     IO.puts "Bye Bye" 
    end 
    end 
end 

Auch http://erlang.org/doc/efficiency_guide/processes.html#id69762

+0

Awesome, Dank sehen! Diese Erlang Docs waren genau das, wonach ich suchte. – greggreg

+0

Ich gehe davon aus, dass das gleiche für "case" usw. gilt? Dass eine Funktion, die rekursiv aus einer Bedingung in "case" aufgerufen wird, TCO ist, vorausgesetzt, dass nach dem Block nichts aufgerufen wird? – kevlarr

+0

Ja, das sollte auch in diesen Situationen gelten. –

Verwandte Themen