In comp.programming someone posted a nice one-liner in Perl to print only
the first appearance of any input line while preserving the order (most
times this is done by sorting and then using uniq-command, but that
destroys the original order). The line is:
perl -ne 'print $_ unless $seen{$_}++'
Nice one. - Someone else then replied with awk:
awk '!o[$0]++'
WOW! - But using the existence of an array variable and simultanously
setting it, reminded me of Tcls arrays, so I tried my best, to make it as
short as possible (and as nasty and unreadable as possible...):
while {0>[gets stdin l]?[exit]:1&[append o($l) 1]==1?"[puts $l]1":1} {}
Doesn't even look like Tcl, does it? But I can't get it shorter. Anyone out
there, who beats this? Just for fun...
Regards
Stephan
I replaced [exit] with 0, and removed the redundant 1& :
while {0>[gets stdin l]?0:[append o($l) 1]==1?"[puts $l]1":1} {}
while {0>[gets stdin l]?0:[append ($l) 1]==1?"[puts $l]1":1} {}
Damn... The exit was a relict of a previous try, 0 is of course much better.
But the anonymous array is really cool... ;-)
btw, two more bytes saved:
while {0>[gets stdin l]?0:[append ($l) 1]==1?"[puts $l]1":1} {}
Stephan
> while {0>[gets stdin l]?0:[append ($l) 1]==1?"[puts $l]1":1} {}
Of course, that's what you already posted. I think, I should read before
replying... ;-)
<mumble>must find shorter...</mumble>
Stephan
I know, totally unsexy, because readable, but still 3 bytes shorter:
while {[gets stdin l]>=0} {if {[append $l 1]==1} {puts $l}}
And if incr would actually auto-initialize a variable as proposed a
while ago here on c.l.t you could save another 4 bytes ;-)
while {[gets stdin l]>=0} {if {[incr $l]==1} {puts $l}}
Ronnie
> suchenwi wrote:
>> Another byte saved, by using the "anonymous array", and more
>> mysterious:
>>
>> while {0>[gets stdin l]?0:[append ($l) 1]==1?"[puts $l]1":1} {}
>>
One byte shorter than this (this time really...):
while {0>[gets stdin l]?0:[append ($l) 1]<2?"[puts $l]1":1} {}
> I know, totally unsexy,
Not at all. The only "drawback" is, that it somehow feels longer because of
using both the condition and the command part of while.
> because readable, but still 3 bytes shorter:
>
> while {[gets stdin l]>=0} {if {[append $l 1]==1} {puts $l}}
And much more obvious!
> And if incr would actually auto-initialize a variable as proposed a
> while ago here on c.l.t you could save another 4 bytes ;-)
>
> while {[gets stdin l]>=0} {if {[incr $l]==1} {puts $l}}
Yes, I thought of this, too. Isn't auto-initialize for incr a TIP?
Stephan
> I know, totally unsexy, because readable, but still 3 bytes shorter:
>
> while {[gets stdin l]>=0} {if {[append $l 1]==1} {puts $l}}
I just see, that you only can save one byte, because you have to use an
array variable. Otherwise a line containing e.g. tcl_platform would kill
the script.
Stephan
> I know, totally unsexy, because readable, but still 3 bytes shorter:
>
> while {[gets stdin l]>=0} {if {[append $l 1]==1} {puts $l}}
Sorry... Saving one more byte from yours:
while {[gets stdin l]+1} {if {[append $(l) 1]==1} {puts $l}}
Stephan
And if we put unknown into play, we can abbreviate commands :-)
wh {[ge stdin l]+1} {if {[ap $l 1]==1} {pu $l}}
George
we can drop the braces in the if condition here:
while {[gets stdin l]>=0} {if [append ($l) 1]==1 {puts $l}}
You need the parentheses around "$l" in the append command -- otherwise
it breaks if you have a line with a single "l"
Or, using http://wiki.tcl.tk/17599
tcl.tcl -n -e 'if [append ($l) 1]==1 {puts $l}'
Compared to the original example:
perl -ne 'print $_ unless $seen{$_}++'
--
Glenn Jackman
"You can only be young once. But you can always be immature." -- Dave Barry
package require 1liners
42
uwe
The parentheses are there to make sure the lines are dumped into the
"anonymous array" as keys, where the values are a string of 1s, one
for each occurrence.
Let's avoid such things for now, since adding code to make the minimal
code work as a script takes the overall length up. My shortest so far
is this:
while {[gets stdin l]+1} {if [incr ($l)]==1 {puts $l}}
It does require 8.5 for auto-increment; without that, you have to use
[append] and that makes the code several characters longer.
The startling thing is how close to good style the shortest version
seems to be. :-)
Donal.
> package require 1liners
Excellent! Will this be already in 8.5 or do we have to wait for 9.0?
Thanks people, you've made my day...
Stephan
> The startling thing is how close to good style the shortest version
> seems to be. :-)
Yes, this is the only thing, which totally disappoints me here. It is short
as it can get, AND its readable (even if you don't know Tcl very well). The
Perl-Guys will never respect Tcl'ers... ;-)
Stephan
> Or, using http://wiki.tcl.tk/17599
> tcl.tcl -n -e 'if [append ($l) 1]==1 {puts $l}'
Yes, but where do you get the lines from stdin from...?
Nevertheless, I like the idea of having a "-e"-switch to tclsh, as many
other shells, not only Perl, have. Myself and many other Tcl'ers seem to
have written a wrapper script for that purpose, to be able to write
something like:
tcl 'puts "hello world"'
Would be nice, if tclsh had such an option.
Stephan
I don't think you need an array. Omitting the ( ) just creates scalar
variables, which isn't all that bad (unless you plan to use the
interpreter for other stuff too ;-). However the single 'l' line would
break the script as Glen correctly points out.
A compromise would be to use (on Donal's unabbreviated short version)
while {[gets stdin l]+1} {if [incr f$l]==1 {puts $l}}
This save one byte of the parenthesis, but prefixes all used variables
with an 'f' (could be any other character, but the 'l' which then would
clash with the l variable used for the line in the case of an empty line.
However, not as beautiful as with (), because an arbitrary character is
introduced and it's less readable.
Ronnie
> I don't think you need an array.
You do. Try your version (
while {[gets stdin l]+1} {if [append $l 1]==1 {puts $l}}
) with an input like this
one
one
two
tcl_platform
three
three
The line containing tcl_platform kills the script, because tcl_platform is a
pre-defined array variable, where you cannot append to.
> A compromise would be to use (on Donal's unabbreviated short version)
>
> while {[gets stdin l]+1} {if [incr f$l]==1 {puts $l}}
>
> This save one byte of the parenthesis, but prefixes all used variables
> with an 'f' (could be any other character, but the 'l' which then would
> clash with the l variable used for the line in the case of an empty line.
>
> However, not as beautiful as with (), because an arbitrary character is
> introduced and it's less readable.
Yes, it works, but will fail when used in conjunction with other code which
contains an array variable starting with f and the contents of one input
line. Okay, that's very unlikely and if one uses obscure oneliners in
combination with clean code in a script somehow did not understand
the "spirit" of oneliners...
Stephan
while {[gets stdin l]+1} {if [incr ($l)]<2 {puts $l}}
And again one char saved.
> It does require 8.5 for auto-increment; without that, you have to use
> [append] and that makes the code several characters longer.
I want to make it absolutely clear, that this was
not my motivation for the auto-init-incr TIP. :-)
> while {[gets stdin l]+1} {if [incr ($l)]<2 {puts $l}}
>
> And again one char saved.
while {~[gets stdin l]} {if [incr ($l)]<2 {puts $l}}
And another one... ;-)
I wonder, if this oneliner will disappear at last and do its job by quantum
mind reading only...
Stephan
if your going to add a character - you should pick
an ugly one like @ or % or ^ or & or * or ! or ~
that way you can confuse people into thinking tcl has magic
behavior and they'll wonder what @$l means ;)
while {[gets stdin l]+1} {if [incr %$l]<2 {puts $l}}
Bruce
Good idea, get some respect from Perl-People... I replaced l with _:
while {~[gets stdin _]} {if [incr ($_)]<2 {puts $_}}
Stephan
This prefix version isn't good. try with this input:
hello
hello(foo)
gotta stick to parens.
Apropos sticking to parens:
just a single open paren as a prefix might actually work!
while {~[gets stdin _]} {if [incr ($_]<2 puts\ \$_}
(replacing the braces for the "then" block with backslashing
space and dollar doesn't reduce size, but readability :-)
Oh, you mean like this:
rmmadwim -nocomplain
Donal.
Isn't "-nocomplain" the option for glob not to say anything, if nothing is
found, i.e. no mind...? Hey! ;-)
Stephan
No, it won't. Consider the input:
hello
hello)
You cannot construct a scalar variable whose name contains an open
paren and ends with a close paren. You can also get into trouble
with :: in the string, such as:
not_a_namespace::fubar
Donal.
I actually think of it meaning not to complain at what it finds in the
mind, but if the cap fits... :-)
Donal.
Actually, it looks just like this is no problem:
set (abc 42; set (abc ;# -> 42
set (abc) 43; set (abc) ;# -> 43
So, some of the stuff created will be local variables, the rest
will be keys for the empty-named array, but all is distinguishable.
> You can also get into trouble with :: in the string, such as:
> not_a_namespace::fubar
This is unfortunately correct.
set (a::b 42 ;# -> can't set "(a::b": parent namespace doesn't exist
damn. :-/
shan't we autocreate namespaces... (NOT!)
Well, do it the other way around:
echo 'puts "hello world"' | tclsh
kind regards
--
Matthias Kraft
Software AG, Germany
(They that can give up essential liberty to obtain a little temporary)
(safety deserve neither liberty nor safety. -- Benjamin Franklin)
This works just as long as the script is not meant to read data from
stdin itself.