Skip to content

Instantly share code, notes, and snippets.

@dramsay
Last active September 14, 2015 12:36
Show Gist options
  • Save dramsay/cf1f0fd425b1d9b376ae to your computer and use it in GitHub Desktop.
Save dramsay/cf1f0fd425b1d9b376ae to your computer and use it in GitHub Desktop.
defmodule Cons do
def each_cons(list, num) when length(list) < num, do: []
def each_cons(list, num) do
[ _ | tail ] = list
cons = Enum.take(list, num)
[ cons | each_cons(tail, num) ]
end
end
iex(1)> Cons.each_cons(String.to_char_list("abacabaca"), 4)
['abac', 'baca', 'acab', 'caba', 'abac', 'baca']
iex(2)> Cons.each_cons(String.to_char_list("abacabaca"), 3)
['aba', 'bac', 'aca', 'cab', 'aba', 'bac', 'aca']
iex(3)> Cons.each_cons(String.to_char_list("abacabaca"), 2)
['ab', 'ba', 'ac', 'ca', 'ab', 'ba', 'ac', 'ca']
@gvaughn
Copy link

gvaughn commented Sep 14, 2015

Doug, yes that version of Dave's you posted in the other gist is what I have pretty much verbatim. The only syntax issue I see is that the double slash (//) used to mean a default parameter. That syntax was later changed to be double backslash(\).

There's also been another addition to the standard library since then, so I wouldn't use that today. Instead use:

Enum.chunk('abacabaca', 4, 1)

That third parameter is an offset. When not supplied explicitly it's the same as the 2nd parameter -- the chunk size.

That said, there's still value in writing something like this yourself as a learning exercise. As I mentioned on the mailing list, the most obvious difference is that Dave used an explicit accumulator result. Upon looking at it more closely, I do see a deeper issue. Dave's solution is properly tail recursive -- the very last step is a function call. The VM is optimized at at a low level and treats this as a jump and will not create a new stack frame. In your solution the last step is to construct a list. Therefore each of your intermediate values are stored on the stack and could overflow. Dave's explicit recursive use of result and Enum.reverse at the end is a common pattern in tail recursion. Enum.reverse looks inefficient, but the VM has heavily optimized that operation because it's a common pattern with tail recursion.

Any chance you're going to make it to ElixirConf in a couple of weeks? I'd love to hear more about your thoughts and plans with Elixir.

@dramsay
Copy link
Author

dramsay commented Sep 14, 2015

Great feedback - thanks! I should have caught the syntax issue with default parameters. Just getting started with Elixir but really liking it.

I would love to go to ElixirConf sometime, but unfortunately won't be making it this year. Hope to catch up with you soon, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment