proc x { l } {
foreach e $l {
set a($e) 1
}
return [llength [array names a]]
}
Not too bad, I guess. Any other thoughts?
Chris
Probably not very helpful but tclx has a function (lrmdups) wich removes
duplicate values from lists so you could say:
llenght [lrmdups "1 2 2 3 3"]
=> 3
Aleks
Hi Chris,
Maybe llength [lsort -unique $lst] ?
Hm, on second thought, while this is shorter to write (and hence may
be deemed more "elegant"), it will probably be less efficient in most
cases - sorting having a complexity of O(n log(n)) (IIRC), anyway,
worse than O(n). So your above approach with a complexity of O(n)
isn't "too bad".
HTH
Helmut Giese
Why the need for an elegant function? Does this one not perform well
enough for your specific need? Or maybe the right question is, why do
you not think that's an elegant solution?
Given a 8000 element list with each item having 8 non-sequential
duplicates, the above takes only 8ish ms on my middle-of-the-road
laptop. That seems pretty good for any sort of data I typically
manipulate. Are you dealing with huge amounts of data, or have to call
this function in a big loop?
If you want a one-liner (if "one-liner == elegant"), you can try this:
[llength [lsort -unique $list]]
... but that, somewhat surprisingly, takes longer than your brute force
solution on the same data set (11ms vs 8ms).
proc x {l} {
llength [lsort -unique $l]
}
Michael
> I'm looking for an elegant and efficient way to determine how many
> distinct elements are in a list. For example [x [list 1]] and [x
> [list 1 1]] and [x [list 1 1 1 1]] should all return 1, [x [list 1 2]]
> and [x [list 1 2 1]] should return 2. One brute force approach that
> suggests itself (untested) is:
>
> proc x { l } {
> foreach e $l {
> set a($e) 1
> }
> return [llength [array names a]]
> }
The only slight improvement I can imagine would be this:
proc count_uniq {list} {
foreach $list [list [unset list]] break
llength [info locals]
}
I use something similar (without the llength) to find the set of unique
values in a list. It's been able to handle anything I've thrown at it
to date. (The little bit of trickery with [list] is because foreach
insists on having at least one real item in its list of values)
Fredderic
> > I'm looking for an elegant and efficient way to determine how many
> > distinct elements are in a list. For example [x [list 1]] and [x
> > [list 1 1]] and [x [list 1 1 1 1]] should all return 1, [x [list 1
> > 2]] and [x [list 1 2 1]] should return 2. One brute force approach
> > that suggests itself (untested) is:
> Why the need for an elegant function? Does this one not perform well
> enough for your specific need? Or maybe the right question is, why do
> you not think that's an elegant solution?
The general view of elegance seems to be either code efficiency, or
code readability. I tend to switch views depending on what I'm writing
at the time. For a small generic function that does a specific task
which could prove handy in all sorts of unforeseen circumstances (and
who's total purpose is summed up in its entirety by the procedures
one-word name alone), I like to go for the efficiency definition. For a
more complex task used in specific cases as part of a larger process, I
go for code readability.
Besides which, in this case (typical of this sort of generic utility
function), I find the most "readable" versions to be only slightly
easier to read than any of the highly efficient and "unreadable"
versions I could imagine off the top of my head anyhow.
> If you want a one-liner (if "one-liner == elegant"), you can try this:
> [llength [lsort -unique $list]]
> ... but that, somewhat surprisingly, takes longer than your brute
> force solution on the same data set (11ms vs 8ms).
In most cases, the instant you mention the term "sort", efficiency
pretty much goes out the window (unless you can cheat somehow, or
unless the alternative would be very, very ugly indeed). ;)
Fredderic
Are you aware of the time command? It answers that question very easily.
Strangely, I find TkX but not TclX is in [package names]. I imagine
that means my installation is screwed up and I'll look into that.
I like this solution for it's expressiveness: it says what I'm going:
"count the items in the list after removing duplicates".
That expressiveness is one aspect of what I consider "elegant".
Efficienty and maintainability are others.
Thanks all.