Just for fun: Can it be done shorter than this?

33 views
Skip to first unread message

Stephan Kuhagen

unread,
Feb 27, 2007, 6:58:48 AM2/27/07
to
Hello again to some fun comparison of other languages to my favourite one...

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

suchenwi

unread,
Feb 27, 2007, 7:23:16 AM2/27/07
to
On 27 Feb., 12:58, Stephan Kuhagen <nos...@domain.tld> wrote:>
> 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...

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} {}

suchenwi

unread,
Feb 27, 2007, 7:26:34 AM2/27/07
to
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} {}

Stephan Kuhagen

unread,
Feb 27, 2007, 8:36:18 AM2/27/07
to
suchenwi wrote:

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

Stephan Kuhagen

unread,
Feb 27, 2007, 8:48:43 AM2/27/07
to
Stephan Kuhagen wrote:

> 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


Ronnie Brunner

unread,
Feb 27, 2007, 8:52:24 AM2/27/07
to

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

Stephan Kuhagen

unread,
Feb 27, 2007, 9:16:58 AM2/27/07
to
Ronnie Brunner wrote:

> 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


Stephan Kuhagen

unread,
Feb 27, 2007, 9:30:28 AM2/27/07
to
Ronnie Brunner wrote:

> 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

Stephan Kuhagen

unread,
Feb 27, 2007, 9:33:27 AM2/27/07
to
Ronnie Brunner wrote:

> 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

Georgios Petasis

unread,
Feb 27, 2007, 9:47:27 AM2/27/07
to Stephan Kuhagen
O/H Stephan Kuhagen έγραψε:

And if we put unknown into play, we can abbreviate commands :-)

wh {[ge stdin l]+1} {if {[ap $l 1]==1} {pu $l}}

George

Glenn Jackman

unread,
Feb 27, 2007, 10:06:18 AM2/27/07
to
At 2007-02-27 08:52AM, "Ronnie Brunner" wrote:
> 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} {}
> >
>
> I know, totally unsexy, because readable, but still 3 bytes shorter:
>
> while {[gets stdin l]>=0} {if {[append $l 1]==1} {puts $l}}

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

Uwe Klein

unread,
Feb 27, 2007, 10:03:42 AM2/27/07
to

package require 1liners
42

uwe

suchenwi

unread,
Feb 27, 2007, 10:42:48 AM2/27/07
to
On 27 Feb., 16:06, Glenn Jackman <gle...@ncf.ca> wrote:
> You need the parentheses around "$l" in the append command -- otherwise
> it breaks if you have a line with a single "l"

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.

Donal K. Fellows

unread,
Feb 27, 2007, 10:48:56 AM2/27/07
to
Georgios Petasis wrote:
> And if we put unknown into play, we can abbreviate commands :-)
>
> wh {[ge stdin l]+1} {if {[ap $l 1]==1} {pu $l}}

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.

Stephan Kuhagen

unread,
Feb 28, 2007, 12:17:07 AM2/28/07
to
Uwe Klein wrote:

> 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

Stephan Kuhagen

unread,
Feb 28, 2007, 12:21:33 AM2/28/07
to
Donal K. Fellows wrote:

> 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

Stephan Kuhagen

unread,
Feb 28, 2007, 12:31:41 AM2/28/07
to
Glenn Jackman wrote:

> 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

Ronnie Brunner

unread,
Feb 28, 2007, 4:05:02 AM2/28/07
to

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

Stephan Kuhagen

unread,
Feb 28, 2007, 4:42:13 AM2/28/07
to
Ronnie Brunner wrote:

> 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


Andreas Leitgeb

unread,
Feb 28, 2007, 9:00:10 AM2/28/07
to
Donal K. Fellows <donal.k...@man.ac.uk> wrote:
> My shortest so far is this:
> while {[gets stdin l]+1} {if [incr ($l)]==1 {puts $l}}

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. :-)

Stephan Kuhagen

unread,
Feb 28, 2007, 9:18:10 AM2/28/07
to
Andreas Leitgeb wrote:

> 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

Bruce Hartweg

unread,
Feb 28, 2007, 9:25:10 AM2/28/07
to

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

Stephan Kuhagen

unread,
Feb 28, 2007, 9:37:12 AM2/28/07
to
> 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}}

Good idea, get some respect from Perl-People... I replaced l with _:

while {~[gets stdin _]} {if [incr ($_)]<2 {puts $_}}

Stephan

Andreas Leitgeb

unread,
Feb 28, 2007, 10:41:17 AM2/28/07
to
Bruce Hartweg <doNO...@nowhere.com> wrote:
> while {[gets stdin l]+1} {if [incr %$l]<2 {puts $l}}

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 :-)

Donal K. Fellows

unread,
Feb 28, 2007, 10:49:09 AM2/28/07
to
Stephan Kuhagen wrote:
> I wonder, if this oneliner will disappear at last and do its job by quantum
> mind reading only...

Oh, you mean like this:
rmmadwim -nocomplain

Donal.

Stephan Kuhagen

unread,
Mar 1, 2007, 12:21:23 AM3/1/07
to
Donal K. Fellows wrote:

Isn't "-nocomplain" the option for glob not to say anything, if nothing is
found, i.e. no mind...? Hey! ;-)

Stephan

Donal K. Fellows

unread,
Mar 1, 2007, 5:28:00 AM3/1/07
to
Andreas Leitgeb wrote:
> Apropos sticking to parens:
> just a single open paren as a prefix might actually work!

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.

Donal K. Fellows

unread,
Mar 1, 2007, 5:29:31 AM3/1/07
to
Stephan Kuhagen wrote:
> Isn't "-nocomplain" the option for glob not to say anything, if nothing is
> found, i.e. no mind...? Hey! ;-)

I actually think of it meaning not to complain at what it finds in the
mind, but if the cap fits... :-)

Donal.

Andreas Leitgeb

unread,
Mar 1, 2007, 9:40:25 AM3/1/07
to
Donal K. Fellows <donal.k...@man.ac.uk> wrote:
> Andreas Leitgeb wrote:
>> Apropos sticking to parens:
>> just a single open paren as a prefix might actually work!
> No, it won't. Consider the input:
> hello
> hello)

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!)

Matthias Kraft

unread,
Mar 1, 2007, 7:53:20 AM3/1/07
to
Stephan Kuhagen wrote:
> tcl 'puts "hello world"'
>
> Would be nice, if tclsh had such an option.

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)

Andreas Leitgeb

unread,
Mar 3, 2007, 5:05:35 PM3/3/07
to
Matthias Kraft <Matthia...@nospam.softwareag.com> wrote:
> Stephan Kuhagen wrote:
>> tcl 'puts "hello world"'
>> Would be nice, if tclsh had such an option.
> Well, do it the other way around:
> echo 'puts "hello world"' | tclsh
> kind regards

This works just as long as the script is not meant to read data from
stdin itself.

Reply all
Reply to author
Forward
0 new messages