Here is an example of ThreeStarPerl. It is apocryphal, since it was created by a very good abstractor as an exercise to see just how inscrutable working PerlLanguage could be.
sub removeDupesButPreserveOrder () { (%_ = ()) || (return [ grep ( !($_{$_}++), @{$_[0]})]); }While I appreciate the particular skill required to emit this code, I find it most useful as a counter-example. The developers I want to hire are the ones who look at it and say "Yuchh! It might work, but why on EARTH would somebody write it THAT way!". I want the developers whose sense of taste and hunger for abstraction compel them to complain about code like this. -- TomStambaugh
It doesn't work. (Someone forgot to UnitTest.) The @{$_[0]} fragment is a array whose name is the first element of the list to be processed (this array probably won't exist). The extra [ ] brackets mean that this subroutine returns an anonymous array whose first element is the un-duplicated list.
I don't think that's what Someone meant. Someone was assuming removeDupesButPreserveOrder would be passed a reference to an array, returning a reference to the resulting filtered array.
Here's what a real multi-star programmer might write:
sub unDup { %_=(),grep(!$_{$_}++,@_) }...although such a short fragment probably wouldn't become a subroutine. For even more fun, consider:
But that takes an actual array and returns an actual array--so it's not solving the same problem.
sub unDup { grep(!$_{$_}++,@_) unless %_=() }The code above would make a nice interview topic for a Perl programmer: "What's wrong with this code, and how would you rewrite it?"
And the answer should include "It's too obscure".
Here's an answer I might give:
sub removeDuplicates { my %temp = (); return grep(!($temp{$_}++), @_); }That code can be clarified with a better hash name. Same idea, slightly different style...
sub remove_duplicates { my %is_seen; grep {not $is_seen{$_}++} @_; }Post-incrementing an associative array is a very common idiom in Perl (explained in ProgrammingPerl). Using this within grep is less common (grep itself is fairly unusual). Return is not necessary, but it is nice for people who use other languages.--CliffordAdams (author of fine PerlMunitions)
I'd rewrite it like this:
sub unique { my %hash; @hash{@_}=(); return keys(%hash); }Which won't preserve order like the original attempt above, but is concise and clear. -- RossLonstein
I assume you want "$hash{@_}", and are you sure that using a list for a hash key will work this way? No, it's a hash slice: @hash{@_} means the list of elements of %hash identified by the list of hash keys in @_. Assigning the empty list to this makes each element of the hash undef, and the duplicate keys overwrite each other. My style would be more like
sub unique { my %seen, @out; while (my $item = shift) { push @out, $item unless $seen{$item}; $seen{$item} = 1; } return @out; }(This does preserve order.) or (following your paradigm)
sub unique { my %out; while (my $item = shift) { $out{$item} = 1; # duplicates removed by set semantics } return keys %out; }-- KarlKnechtel
Simpler yet:
sub unique { my %out; $out{$_} = 1 for @_; return keys %out; }I've written Remove duplicates discarding order as:
sub uniq { return keys %{ map { $_ => 1 } @_ }; }or, more accurately, I used @array = keys %{ map { $_ => 1 } @array }; more than once, and decided that it deserved a name to document what it does.
See also: ThreeStarProgrammer
sub unique { use v6; (bag @_).List }Hehehehe...That one doesn't preserve order, though.
sub unique { use v6; my %seen of Bool; gather { for @_ { take $_ unless %seen{$_}++; } } }Perfect!