`recur` throws control flow to the nearest `loop` head or fn body, and each
method is itself a function, so `recur` within a method body will simply jump
to the start of the method, _not_ tail-call the multimethod. e.g.:
=> (defmulti foo type)
#'user/foo
=> (defmethod foo Long
[x]
(assert (integer? x) (str "x isn't an integer! " (pr-str x)))
(recur (str x)))
#<MultiFn clojure.lang.MultiFn@4f0ab3f2>
=> (foo 5)
AssertionError Assert failed: x isn't an integer! "4"
(integer? x) user/eval1831/fn--1832 (NO_SOURCE_FILE:3)
What you're trying to do is really a special case of mutual recursion: because
Clojure's methods are separate functions, calling back through the multimethod
(and its dispatch fn) will always consume stack space. The general solution
for this is to use `trampoline`, which will continuously call through functions
returned from calling a function until a non-function value is returned. This
would allow you to make your multimethod mutually-recursive, as long as those
recursive calls are made by returning a function (that the user of the
multimethod would `trampoline` through):
=> (defmethod foo String
[x]
(str "got " x "!"))
#<MultiFn clojure.lang.MultiFn@4f0ab3f2>
=> (defmethod foo Long
[x]
#(foo (str x)))
#<MultiFn clojure.lang.MultiFn@4f0ab3f2>
=> (foo 5)
#<user$eval1888$fn__1889$fn__1890 user$eval1888$fn__1889$fn__1890@3852fdeb>
=> (trampoline foo 5)
"got 5!"
>From an API standpoint, you would presumably name the multimethod itself
>something like `trans*`, and expose only a helper function `trans` that would
>implicitly trampoline through any functions returned by `trans*`. There is a
>cost associated with trampolining isn't zero, but won't swamp actual work as
>long as the methods are doing something nontrivial anyway.
Another option you might consider is using agents. I see your methods already
take a single state argument; this would then make it really easy to lift the
automata onto an agent (or set of agents). That state would become the agents'
state, and "recursive" calls would become a `send` (or `send-off` if you're
doing IO or other blocking things) to the same agent (e.g. `(send *agent*
trans)`).
Cheers,
- Chas
--
http://cemerick.com
[Clojure Programming from O'Reilly](http://www.clojurebook.com)
On Dec 17, 2012, at 11:56 AM, juan.facorro wrote:
> What about recur?
>
> It's a special form used for tail call optimizations.
>
> Juan
>
> On Monday, December 17, 2012 1:32:31 PM UTC-3, bruce li wrote:
> Hello, everyone.
>
> I'm currently trying to model an automata using multi-method. So in my code,
> I have something like:
>
> (defmethod trans :state
> [state]
> ; .......
> (trans ......))))
>
> In the last line, the trans will be called with some different state.
>
> However, it seems that such call still consumes stack and I quickly come to a
> stack overflow error when the states are reached multiple times.
>
> I'm wondering if there is some ways to optimize the code.
>
> Thanks!
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to [email protected]
> Note that posts from new members are moderated - please be patient with your
> first post.
> To unsubscribe from this group, send email to
> [email protected]
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en