Bessere Lösung fürBeispiel?
am 25.10.2006 21:14:07 von Daniel Michalik
Hallo,
mit einer Freundin, die sich Perl grade neu lernt, ist eben ein kleines
Perl-Skript entstanden. Das wurde Schritt für Schritt verfeinert, die
letzte Version findet sich am Ende des Postings (ich denke, es ist kurz
genug dafür).
Was tut das ganze? Man gibt auf STDIN nacheinander, durch Whitespace
getrennt, jeweils einen (nicht eindeutigen) Key und einen Value ein. Das
Skript baut daraus einen Hash von Arrays. Zeilenumbrüche etc. sind für
die Eingabe irrelevant - es gilt immer erstes Wort ist Key, zweites ist
Value. Beispiel (die drei Striche symbolisieren mein EOF, danach folgt
die Ausgabe des Skripts):
| $ ./skript.pl
| foo bar
| foo
| baz x y
| z a z b z c foo
| boz
| ---
| foo: bar baz boz
| x: y
| z: a b c
Die Frage ist: Wie wäre es schöner gegangen, für folgende Werte für
schöner:
1) Lesbarer
2) Kürzer, "more hacking"
Danke für Meinungen!
-daniel-
Und nun das Skript:
| #!/usr/bin/perl -w
| use strict;
|
| my %hash;
| my ($key,$value);
| my @list;
| while(<>){
| chomp $_;
| my @temp = split (/\s+/, $_);
| foreach(@temp){
| push (@list, $_);
| }
| }
|
| while(@list){
| $key = shift(@list);
| $value = shift(@list);
| push (@{$hash{$key}}, $value);
| }
|
| foreach $key(sort keys %hash){
| print "$key: ";
| foreach(@{$hash{$key}}){
| print "$_ "; #Known-bug: Beim letzten Eintrag ein
| #Leerzeichen zu viel.
| }
| print "\n";
| }
(getestet mit 5.8.8)
--
http://www.argafal.de
GPG-Key: 0x53BE81EC
Fingerprint: A854 4C0A FF1A 09DF 5433 0EF8 1CF0 0213 53BE 81EC
Re: Bessere Lösung für Beispiel?
am 25.10.2006 21:38:27 von Frank Seitz
Daniel Michalik wrote:
> | $ ./skript.pl
> | foo bar
> | foo
> | baz x y
> | z a z b z c foo
> | boz
> | ---
> | foo: bar baz boz
> | x: y
> | z: a b c
>
> Die Frage ist: Wie wäre es schöner gegangen, für folgende Werte für
> schöner:
> 1) Lesbarer
> 2) Kürzer, "more hacking"
$/ = undef;
my @arr = split ' ',<>;
my %hash;
while (@arr)
{
my $arr = $hash{shift @arr} ||= [];
push @$arr,shift @arr;
}
while (my ($key,$arr) = each %hash)
{
print "$key: @$arr\n";
}
Grüße
Frank
--
Dipl.-Inform. Frank Seitz; http://www.fseitz.de/
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel
Re: Bessere Lösung für Beispiel?
am 25.10.2006 22:03:19 von Mirco Wahab
Thus spoke Daniel Michalik (on 2006-10-25 21:14):
> Was tut das ganze? Man gibt auf STDIN nacheinander, durch Whitespace
> getrennt, jeweils einen (nicht eindeutigen) Key und einen Value ein. Das
> Skript baut daraus einen Hash von Arrays. Zeilenumbrüche etc. sind für
> die Eingabe irrelevant - es gilt immer erstes Wort ist Key, zweites ist
> Value. Beispiel (die drei Striche symbolisieren mein EOF, danach folgt
> die Ausgabe des Skripts):
...
# einlesen
push @list, split while <>;
# paarweise einsortieren
push @{ $hash{$list[$_*2]} }, $list[$_*2+1] for 0 .. $#list/2;
# ausgeben
print "$_: @{ $hash{$_} }\n" for sort keys %hash;
...
Naja, je nach Geschmack ;-)
(Für mich ist das lesbarer als Deines ...)
Viele Grüße
M.
Re: Bessere Lösung fürBeispiel?
am 25.10.2006 22:30:09 von hjp-usenet2
On 2006-10-25 19:14, Daniel Michalik wrote:
> Die Frage ist: Wie wäre es schöner gegangen, für folgende Werte für
> schöner:
> 1) Lesbarer
> 2) Kürzer, "more hacking"
Mit Betonung auf "lesbarer", aber etwas kürzer wird es dabei auch:
Schritt 1: Das File hat keine Zeilenstruktur. Wenn es also nicht allzu
lang ist, kann man es als eine einzige lange Zeile behandeln:
#v+
--- /home/hjp/tmp/daniel 2006-10-25 21:52:16.000000000 +0200
+++ /home/hjp/tmp/daniel2 2006-10-25 21:55:13.000000000 +0200
@@ -4,13 +4,10 @@
my %hash;
my ($key,$value);
my @list;
-while(<>){
- chomp $_;
- my @temp = split (/\s+/, $_);
- foreach(@temp){
- push (@list, $_);
- }
-}
+{
+ local $/;
+ @list = split /\s+/, <>;
+}
while(@list){
$key = shift(@list);
#v-
Schritt 2: Die Schleife zum Ausgeben der Arrays kann man durch join
ersetzen. Nebenbei beseitigt das den "known bug":
#v+
--- /home/hjp/tmp/daniel2 2006-10-25 21:55:13.000000000 +0200
+++ /home/hjp/tmp/daniel3 2006-10-25 22:00:19.000000000 +0200
@@ -16,10 +16,5 @@
}
foreach $key(sort keys %hash){
- print "$key: ";
- foreach(@{$hash{$key}}){
- print "$_ "; #Known-bug: Beim letzten Eintrag ein
- #Leerzeichen zu viel.
- }
- print "\n";
+ print "$key: ", join(" ", @{$hash{$key}}), "\n";
}
#v-
Schritt 3: Ein paar eher kosmetische Ãnderungen: Wenn Variablen nur dort
definiert sind, wo sie gebraucht werden, erhöht das die
Ãbersichtlichkeit.
#v+
--- /home/hjp/tmp/daniel3 2006-10-25 22:00:19.000000000 +0200
+++ /home/hjp/tmp/daniel4 2006-10-25 22:03:40.000000000 +0200
@@ -1,20 +1,20 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
use strict;
+use warnings;
-my %hash;
-my ($key,$value);
my @list;
{
local $/;
@list = split /\s+/, <>;
}
+my %hash;
while(@list){
- $key = shift(@list);
- $value = shift(@list);
+ my $key = shift(@list);
+ my $value = shift(@list);
push (@{$hash{$key}}, $value);
}
-foreach $key(sort keys %hash){
+foreach my $key (sort keys %hash){
print "$key: ", join(" ", @{$hash{$key}}), "\n";
}
#v-
Schritt 4: Das Einlesen des Files muss besser gehen, als zuerst alles in
ein Array und dann in ein Hash. Eigentlich will man ja immer Wort-Paare
lesen und das geht mit mit einer Regex (aber der /g Modifikator ist
etwas tückisch, darum habe ich dafür länger gebraucht):
#v+
--- /home/hjp/tmp/daniel4 2006-10-25 22:03:40.000000000 +0200
+++ /home/hjp/tmp/daniel5 2006-10-25 22:16:10.000000000 +0200
@@ -2,19 +2,15 @@
use strict;
use warnings;
-my @list;
+my %hash;
{
local $/;
- @list = split /\s+/, <>;
+ $_ = <>;
+ while (/(\S+)\s+(\S+)/g) {
+ push (@{$hash{$1}}, $2);
+ }
}
-my %hash;
-while(@list){
- my $key = shift(@list);
- my $value = shift(@list);
- push (@{$hash{$key}}, $value);
-}
-
foreach my $key (sort keys %hash){
print "$key: ", join(" ", @{$hash{$key}}), "\n";
}
#v-
Schritt 5: Das join ist eigentlich auch überflüssig - Spaces werden (per
default) ohnehin von der String-Interpolation von Arrays eingefügt:
#v+
--- /home/hjp/tmp/daniel5 2006-10-25 22:16:10.000000000 +0200
+++ /home/hjp/tmp/daniel6 2006-10-25 22:19:33.000000000 +0200
@@ -12,5 +12,5 @@
}
foreach my $key (sort keys %hash){
- print "$key: ", join(" ", @{$hash{$key}}), "\n";
+ print "$key: @{$hash{$key}}\n";
}
#v-
Somit ist das Script jetzt von 28 auf 16 Zeilen geschrumpft, ohne
(meiner Meinung nach) unleserlicher geworden zu sein:
#v+
#!/usr/bin/perl
use strict;
use warnings;
my %hash;
{
local $/;
$_ = <>;
while (/(\S+)\s+(\S+)/g) {
push (@{$hash{$1}}, $2);
}
}
foreach my $key (sort keys %hash){
print "$key: @{$hash{$key}}\n";
}
#v-
--
_ | Peter J. Holzer | > Wieso sollte man etwas erfinden was nicht
|_|_) | Sysadmin WSR | > ist?
| | | hjp@hjp.at | Was sonst wäre der Sinn des Erfindens?
__/ | http://www.hjp.at/ | -- P. Einstein u. V. Gringmuth in desd
Re: Bessere Lösung für Beispiel?
am 25.10.2006 23:06:24 von Mirco Wahab
Thus spoke Mirco Wahab (on 2006-10-25 22:03):
> Naja, je nach Geschmack ;-)
Nachdem ich Peters Beitrag las (shift), ist
mir was schönes *und* kompaktes dazu eingefallen,
das ganze Programm sähe demnach so aus:
use strict;
use warnings;
my (%hash, @list);
# einlesen
push @list, split while <>;
# einsortieren
push @{ $hash{ shift @list } }, shift @list while @list;
# ausgeben
print map "$_: @{ $hash{$_} }\n", sort keys %hash;
Viele Grüße
Mirco