-
Website
-
Original page
http://blog.adambachman.org/2008/10/simple-permutations-in-python-and-ruby.html -
Subscribe
All Comments -
Community
-
Top Commenters
-
abachman
3 comments · 1 points
-
Aldric
2 comments · 1 points
-
Craigslist PVA
1 comment · 1 points
-
Mike Subelsky
1 comment · 1 points
-
-
Popular Threads
permutations(li.select() {|n| n != element}) \
{|val| yield([element] << val)}
Now, what this is saying is ...
run the permutation function on every element in the list except the current one, and run that against ... What, exactly? What is "yield(|element << val)" ? I can't find a good way to put this in english..
permutations(li.select() {|n| n != element}){|val| yield([element] << val)}
I think you've got the
li.select()chunk. I'm passing the list that was given as input after removingelement. The block following the function call is handed down to the next level. Since yield executes the block that was passed in, I'm asking the next level down to execute the block that was passed in to the current level. The block that's passed down internally is different than the block that's passed externally, so we're dealing with two possible behaviors for the yield call.The recursive call passes a new block to the next level down containing a reference to the block that was passed in to the current level, so every level under the top level (level 0, below) passes a block containing the accumulator function (
[element] << val). The internal yield call accumulates the "returned" values from the descendent calls (calls that are at deeper levels of recursion). It may be easier to think of the yield calls in the Ruby and Python versions as almost likereturnstatements. In Ruby, instead of doing something with the returned value outside the function, we pass a block of code into the function that we want to execute every time the function "returns" (hits ayieldstatement).Here's what the flow of the function looks like:
# level 0
=> permutations([1,2,3]) {|n| puts n}
element = 1
yield = {|n| print n}
# level 1
===> permutations([2,3]) {|val| yield#level0([1] << val}
element = 2
yield = {|val| yield#level0([1] << val)}
# level 2
=====> permutations([3]) {|val| yield#level1([2] << val)}
call([2] << 3)
return [2,3]
# level 1
===> (execute yield)
call([1] << [2,3]) # BUG!
# level 0
=> (execute yield)
call(print [1, [2, 3]])
# level 1
===> (continue li.each loop)
element = 3
yield {|val| yield#level0([1] << val)}
# level 2
=====> permutations([2]) {|val| yield#level1([3] << val)}
call([3] << 2)
return [3,2]
# level 1
===> (execute yield)
call([1] << [3,2]) # BUG!
# level 0
=> (execute yield)
call(print [1, [3, 2]])
(continue li.each loop)
element = 2
yield = {|n| print n}
etc...
Wow, that may be a lot more confusing than helpful, but I managed to spot a bug, so it's worthwhile to me at least :|.
[a] << bappends `b` to the list containing a single element, `a`. The bug is that if `b` is a list, we end up with [1, [2, 3]]. You can change{|val| yield([element] << val)}to{|val|to fix that.yield(([element] << val).flatten)}
I hope I have helped a little bit. I at least hope I haven't driven you to despair. You could try looking for an algorithm that generates permutations iteratively, instead of recursively, and code that in Ruby. Then this recursive version might make more sense.
On the other hand, using
[element].concat (val)
got me a result in ~25 seconds (only two runs, so not conclusive). Since this is Ruby, this should work on most anything that's enumerable.. Even sets (just tested it), however worthless that may be on a set.
This, I must admit, is by far the most elegant permutation implementation I've found on the web. Do you mind if I keep it, add your name/handle/URL to the source and reuse / distribute as I see fit (Not that I expect to find too many people who need to permute in Ruby/Python, but who knows) ?
And, to answer, yes, this was very helpful. It's really the "plain" permutation system, but quite ruby-ified with a flavor of LISP ;-)
Thanks for your explanation!