> Just to clarify: I dont' care what the type of the output is. I guess
> (seq [[]]) would actually be more consistent with the existing
> function. The point is that the output of 0-arg combinations should
> be a seq containing an empty vector (or whatever other types, I don't
> care), not an empty seq.
>
> Anyone willing to sign off??
Please do enter it as an issue. I'd be interested in hearing from
Chouser before making the change. He added combinations to lazy-seqs.
Thanks!
--Steve
> I'd be interested in hearing from Chouser before making the change.
> He added combinations to lazy-seqs.
I think it's quite cool that simply removing the "when" accomplishes
this.
--Steve
Yes, exactly :) Returning nil for no-args was "hacked on" in a sense,
in that the loop would have naturally done the "right thing" otherwise.
Issue here: http://code.google.com/p/clojure-contrib/issues/detail?id=6
Thanks,
Jason
I did what now? My memory must be going. Here are some other
implementations I apparently went through:
http://paste.lisp.org/display/68701
Reviewing the IRC log from that day, it appears I put no particular
thought into then no-args case, so I'm happy to defer to Jason's logic
on the matter. This is the best version?
(defn combinations
"Takes any number of seqs and returns a lazy list of ordered
combinations (pick 1 from each seq). See also 'for'"
[& seqs]
(if (empty? seqs) '(())
(for [item (first seqs)
more (apply combinations (rest seqs))]
(cons item more))))
--Chouser
Depends on your definition of "best".
I just posted a version at http://paste.lisp.org/display/74134 which
is many times faster.
Also, I have to say that calling this function "combinations" is
fairly nonstandard. Usually combinations refers to all the ways of
taking n elements from a given collection.
So (combinations [1 2 3] 2) would give you back ((1 2) (1 3) (2 3)).
selections seems like a better name to me for this particular function.
I haven't had a chance to look at that in detail yet, but I'm 100% in
favor of faster.
> Also, I have to say that calling this function "combinations" is
> fairly nonstandard. Usually combinations refers to all the ways of
> taking n elements from a given collection.
>
> So (combinations [1 2 3] 2) would give you back ((1 2) (1 3) (2 3)).
That had nagged me a bit too ... I agree.
> selections seems like a better name to me for this particular function.
I think the usual mathematical name is be cartesian-product (or maybe
cross-product), although selections is OK with me too.
-Jason
Yes, now that you've reminded me, I agree that cartesian-product is
the usual mathematical name, and that would probably be my first
choice.
Try running on an extreme input like:
["ACDFG" "A" "B" "C" "D" "E" "F" "ABCD" "G" "H" "I" "ABEG" "J" "K"
"BCDE" "L" "ABCDG" "M" "EF" "NABC" "ABCDFG" "ABDEFG" "DGHI" "ABCDEFG"]
and let me know if you also see the 2x performance difference.
Yes, yours runs more than 2x faster on this input. On the other hand,
mine runs almost 2x faster on
(range 3000) (range 3000) ... As far as I can tell, yours is faster
when there are a large number of input seqs, especially if any of them
have 0 or 1 elements. Anyway, I'm happy with either implementation,
although probably they should take multiple seq arguments rather than
a seq of seqs?
-Jason
Good point ... I shouldn't have tried to avoid adding the "let":
(defn combinations "Take a seq of seqs and return a lazy list of
ordered combinations (pick 1 from each seq)"
[seqs]
(if (empty? seqs) '(())
(let [rec (combinations (rest seqs))]
(for [item (first seqs)
rst rec]
(cons item rst)))))
>
> I've tested that already, and it takes even longer for all but trivial
> inputs, because rec now prevents the combinations sequence from being
> garbage collected, and the memory allocation bogs things down
> tremendously.
Ah, I noticed the unpredictable timings too but didn't make that
connection -- thanks. OK, I'm happy to go with your version then :)
-Jason
http://paste.lisp.org/display/74134#3
I'll keep playing around to see if I can get it faster, but so far,
this is the fastest way I've found to generate the cartesian product
in standard order.
Hah, that would have been funny if after all this, the original point
of the thread got lost :). Anyway, thanks for all your hard work on
this! It's been very interesting for me, since I think I finally
understand (at least somewhat) these "iterative" versions of things
I've seen floating around.
-Jason