Thanks José! Dang, I knew somewhere that I needed to keep the rest of the list when I was in a subtree, but I couldn’t get it to return properly. Thanks for explaining how you reasoned it out starting with the traversal pattern, that was very helpful.
-Brian > On Aug 28, 2016, at 1:55 PM, José Valim <[email protected]> > wrote: > > Let's start with the regular traversal "template": > > defmodule Grouper do > def group(list) do > group(list, []) > end > > defp group([h | t], acc) do > group(t, [h | acc]) > end > defp group([], acc) do > Enum.reverse(acc) > end > end > > The code above allow us to traverse the whole list and return it as it was. > > Now we need to add a clause to group/2 that detects :loop_begin and starts > the looping construct: > > defp group([:loop_begin | t], acc) do > {current, rest} = loop(t, [:loop_begin]) > group(rest, [current | acc]) > end > > The loop construct is similar to group, it receives a list and an accumulator > (which starts with the value of [:loop_begin]) and will traverse the list > until :loop_end is found, returning the grouped loop and the remaining data: > > defp loop([:loop_end | t], acc) do > {Enum.reverse([:loop_end | acc]), t} > end > defp loop([h | t], acc) do > loop(t, [h | acc]) > end > > Notice I did not add a loop([], acc) clause. If the list terminates without a > loop_end, it should fail. This allows us to handle a loop but not a loop > within a loop. To do so, we would need tp add a similar loop_begin clause as > we did to group: > > defp loop([:loop_begin | t], acc) do > {current, rest} = loop(t, [:loop_begin]) > loop(rest, [current | acc]) > end > > Overall we have: > > defmodule Grouper do > def group(list) do > group(list, []) > end > > defp group([:loop_begin | t], acc) do > {current, rest} = loop(t, [:loop_begin]) > group(rest, [current | acc]) > end > defp group([h | t], acc) do > group(t, [h | acc]) > end > defp group([], acc) do > Enum.reverse(acc) > end > > defp loop([:loop_begin | t], acc) do > {current, rest} = loop(t, [:loop_begin]) > loop(rest, [current | acc]) > end > defp loop([:loop_end | t], acc) do > {Enum.reverse([:loop_end | acc]), t} > end > defp loop([h | t], acc) do > loop(t, [h | acc]) > end > end > > We could merge loop/2 and group/2 into a single function but, for such, we > would need to make them return the same type (one returns a tuple, the other > returns a list). > > José Valim > www.plataformatec.com.br <http://www.plataformatec.com.br/> > Skype: jv.ptec > Founder and Director of R&D > > On Sun, Aug 28, 2016 at 8:20 PM, Brian Bugh <[email protected] > <mailto:[email protected]>> wrote: > I'm incredibly stuck on something that would be very easy in imperative, but > I just can't figure it out with Elixir in a properly functional way. I want > to turn this: > > [:input, :loop_begin, :output, :loop_begin, :decrement, :loop_end, :input, > :loop_end] > > into this: > > [:input, [:loop_begin, :output, [:loop_begin, :decrement, :loop_end], :input, > :loop_end]] > > Basically - any time there's a :loop_begin it makes a new list, and then > closes the list with :loop_end. > > This is the closest that I've gotten, but it (obviously) stops as soon as it > hits the first :loop_end. > > def tree(tokens), do: do_tree(tokens, []) > > defp do_tree([:loop_begin|rest], acc) do > [tree(rest, [:loop_begin]) | acc] > end > > defp do_tree([:loop_end|rest], acc) do > [:loop_end, acc] > end > > defp do_tree([t|rest], acc), do: map(rest, [t | acc]) > > It's like I need to consume items sometimes but not others, and I can't > figure it out. Every variation I've come up with in the last couple of hours > has been a flop. > > Any suggestions? This seems like something that would be obvious if I had > more functional experience. > > Thanks! > > -Brian > > -- > You received this message because you are subscribed to the Google Groups > "elixir-lang-talk" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected] > <mailto:[email protected]>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-talk/beb86ff0-be20-462c-8996-14e251201983%40googlegroups.com > > <https://groups.google.com/d/msgid/elixir-lang-talk/beb86ff0-be20-462c-8996-14e251201983%40googlegroups.com?utm_medium=email&utm_source=footer>. > For more options, visit https://groups.google.com/d/optout > <https://groups.google.com/d/optout>. > > > -- > You received this message because you are subscribed to a topic in the Google > Groups "elixir-lang-talk" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/elixir-lang-talk/UHOIVvmUS4s/unsubscribe > <https://groups.google.com/d/topic/elixir-lang-talk/UHOIVvmUS4s/unsubscribe>. > To unsubscribe from this group and all its topics, send an email to > [email protected] > <mailto:[email protected]>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/elixir-lang-talk/CAGnRm4LmeQj2_anpG4AG%2BOTBoTThnSMZDJBHAGCWrJsNKC88aQ%40mail.gmail.com > > <https://groups.google.com/d/msgid/elixir-lang-talk/CAGnRm4LmeQj2_anpG4AG%2BOTBoTThnSMZDJBHAGCWrJsNKC88aQ%40mail.gmail.com?utm_medium=email&utm_source=footer>. > For more options, visit https://groups.google.com/d/optout > <https://groups.google.com/d/optout>. -- You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/78C2CABF-B18E-4226-B8F5-CFFD9236DB5C%40gmail.com. For more options, visit https://groups.google.com/d/optout.
