Nicht-rekursive Rekursion

Nicht-rekursive Rekursion

am 19.09.2006 16:03:09 von Mark Neis

Hallo Group,

das unten angehängte Skript soll rekursiv eine Verzeichnishierarchie
hinabsteigen und die dort gefundenen Verzeichnisse/Dateien auflisten,
zusammen mit der "Tiefe", in der sie gefunden wurden.

Leider hört die Rekursion nach dem Absteigen ins erste Verzeichnis
auf, obwohl noch weitere Unterverzeichnisse vorhanden sind.

Beispiel:


+- erdgeschoss
|
+ - keller
| |
| + - parkdeck_u2
| | |
| | + - auto1
| | + - auto2
| |
| + - geruempel
| + - kram
| + - alter_mist
|
+ - besenkammer
| |
| + - handfeger
|
+ - heizung



führt zu folgender Ausgabe:

(0) erdgeschoss
(1) keller
(1) parkdeck_u2
(1) geruempel
(1) kram
(1) alter_mist

Ende. Nada. Zilch. Watt nu?

Ich vermute, dass der Fehler irgendwie mit dem Scope von $irknwas zu
tun hat, aber meine Lektüre diverser Stellen der Oreilly-Bookshelves
brachte mich nicht weiter. Wer gibt mir einen Tipp?

Grüße,
Mark


# ----------------------------------------
#!/usr/bin/perl

sub processdir {
$dir = @_[0];
$level = @_[1];
opendir(DIR, $dir) or die "Can't open $dir";
@files = readdir(DIR);
closedir DIR;

foreach $f (@files) {
next if ($f eq ".");
next if ($f eq "..");

$basename = $f;
$basename =~ s/\/$//;
$basename =~ s/^.*\///;
print "($level) $basename\n";

if ( -d $f ) {
processdir ($f, $level + 1);
}
}
}

$level=0;
processdir(".", 0);
# ----------------------------------------

--
Vererbung ist das, woran man glaubt, wenn man ein intelligentes Kind hat.

Re: Nicht-rekursive Rekursion

am 19.09.2006 16:27:27 von Alexander Bartolich

Mark Neis schrieb:
> [...]
> Leider hört die Rekursion nach dem Absteigen ins erste Verzeichnis
> auf, obwohl noch weitere Unterverzeichnisse vorhanden sind.
> [...]
> sub processdir {
> $dir = @_[0];
> $level = @_[1];
> opendir(DIR, $dir) or die "Can't open $dir";
> @files = readdir(DIR);

my $dir = @_[0];
my $level = @_[1];
my $DIR;
opendir($DIR, $dir) or die "Can't open $dir";
my @files = readdir($DIR);

usw. usf.

--
post tenebras lux. post fenestras tux.

Re: Nicht-rekursive Rekursion

am 19.09.2006 16:30:19 von Mark Neis

Alexander Bartolich schrieb:

>Mark Neis schrieb:
>> [...]
>> Leider hört die Rekursion nach dem Absteigen ins erste Verzeichnis
>> auf, obwohl noch weitere Unterverzeichnisse vorhanden sind.
>> [...]
>> sub processdir {
>> $dir = @_[0];
>> $level = @_[1];
>> opendir(DIR, $dir) or die "Can't open $dir";
>> @files = readdir(DIR);
>
> my $dir = @_[0];
> my $level = @_[1];
> my $DIR;
> opendir($DIR, $dir) or die "Can't open $dir";
> my @files = readdir($DIR);
>

Danke. Meine Vermutung, dass der Scope die Ursache sein könnte, ging
also schon mal in die richtige Richtung.

Mark

--
Im Falle eines Atomkriegs gehe ich in die Schweiz; dort findet alles zwanzig
Jahre später statt als anderswo
[Albert Einstein]

Re: Nicht-rekursive Rekursion

am 20.09.2006 09:35:00 von Michelino Caroselli

Mark Neis wrote:
[...]
> Leider hört die Rekursion nach dem Absteigen ins erste Verzeichnis
> auf, obwohl noch weitere Unterverzeichnisse vorhanden sind.
>
> Beispiel:
[...]
> Ich vermute, dass der Fehler irgendwie mit dem Scope von $irknwas zu
> tun hat, aber meine Lektüre diverser Stellen der Oreilly-Bookshelves
> brachte mich nicht weiter. Wer gibt mir einen Tipp?

Zusätzlich zu dem was Alexander geschrieben hat (ich hatte das gleich
Problem vor ein paar Tagen):

> # ----------------------------------------
> #!/usr/bin/perl
>
> sub processdir {
> $dir = @_[0];
> $level = @_[1];
> opendir(DIR, $dir) or die "Can't open $dir";
> @files = readdir(DIR);

readdir() liefert nur die Namen der Dateien/ Verzeichnisse (s.u.)

> closedir DIR;
Hier solltest du noch ein
chdir($dir) or die "Can't change to $dir";
einfügen

[...]
> if ( -d $f ) {
-d prüft im _aktuellem_ Verzeichnis ob $f ein Verzeichnis ist (was ab der 1.
Ebene nicht mehr funktioniert, außer es existiert zufällig ein Verzeichnis
mit gleichem Namen;)

> processdir ($f, $level + 1);
Hier noch ein
chdir("..");
einfügen

> }
> }
> }
>
> $level=0;
Das benötigst du auch nicht, da es ja in der sub deklariert wird und im
Hauptteil nicht verwendet wird.
Ein 'use warnings;' oder ein '-w' im shebang würde dich darauf aufmerksam
machen.


PS und BTW: Dein Beispiel irritiert. Du schreibst zwar das die Rekursion nur
in die erste Ebene absteigt, listet aber Verzeichnisse der 2. Ebene auf.

HTH und Gruß, Michel
--
Aus Murphy's Gesetze:
Das verdorbene Ei ist das, welches du in den Kuchenteig schlägst.

Re: Nicht-rekursive Rekursion

am 20.09.2006 13:02:26 von struebig

Mark Neis wrote:
> # ----------------------------------------
> #!/usr/bin/perl

ein -w und use strict hätten dir die zmuindest veraltete Schreibweise
die du verwendest bemängelt.

>
> sub processdir {
> $dir = @_[0];
> $level = @_[1];

Wenn schon dann:
$dir = $_[0];
$level = $_[1];

oder

my($dir, $level) = @_;

> opendir(DIR, $dir) or die "Can't open $dir";

Den Grund für einen Fehler kannst du dir auch noch mitteilen lassen

opendir(DIR, $dir) or die "Can't open $dir: $!";

> @files = readdir(DIR);
> closedir DIR;
>
> foreach $f (@files) {

Wie schon erwähnt, erhälst du hier nur die Datennamen ohne Verzeichniss,
d.h. $f sollte besser sowas sein:

foreach (@files) {
my $f = "$dir/$_";

>
> $basename = $f;
> $basename =~ s/\/$//;
> $basename =~ s/^.*\///;

Das liesse sich leichter ermitteln:

$f =~ /.*\/(.*)$/g;
my $basename = $1;

Wobei das Überflüssig ist, da du ja in @files bereits den "basenamen"
hast.

Struppi.

Re: Nicht-rekursive Rekursion

am 20.09.2006 13:04:14 von Mark Neis

Michelino Caroselli schrieb:

[Nützliches gesnippt]

Danke für die Tipps.


>> $level=0;
>Das benötigst du auch nicht, da es ja in der sub deklariert wird und im
>Hauptteil nicht verwendet wird.

Ich habe den Code auf das Wesentliche gekürzt. Das originale Skript
macht noch ein paar Sachen mehr, wo's dann benutzt wird.


>Ein 'use warnings;' oder ein '-w' im shebang würde dich darauf aufmerksam
>machen.

Ack.


>PS und BTW: Dein Beispiel irritiert. Du schreibst zwar das die Rekursion nur
>in die erste Ebene absteigt, listet aber Verzeichnisse der 2. Ebene auf.

Ack. Ich war davon ausgegangen, dass das Skript mit erdgeschoss als
cwd startet, dann steigt es in das erste Subdir. Das hätte ich
explizit erwähnen sollen.


Mark

--
Im Falle eines Atomkriegs gehe ich in die Schweiz; dort findet alles zwanzig
Jahre später statt als anderswo
[Albert Einstein]

Re: Nicht-rekursive Rekursion

am 20.09.2006 13:28:14 von Daniel Fischer

Mark Neis!

> Hallo Group,
>
> das unten angehängte Skript soll rekursiv eine Verzeichnishierarchie
> hinabsteigen und die dort gefundenen Verzeichnisse/Dateien auflisten,
> zusammen mit der "Tiefe", in der sie gefunden wurden.

Also ungefähr sowas?

use File::Find;
find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");

> sub processdir { [...]

Dir fehlt ein my vor @files = ... und vor $f (@files).


Gruß
Daniel

Re: Nicht-rekursive Rekursion

am 20.09.2006 13:57:55 von Mark Neis

Daniel Fischer schrieb:

>Mark Neis:
>> das unten angehängte Skript soll rekursiv eine Verzeichnishierarchie
>> hinabsteigen und die dort gefundenen Verzeichnisse/Dateien auflisten,
>> zusammen mit der "Tiefe", in der sie gefunden wurden.
>
>Also ungefähr sowas?
>
>use File::Find;
>find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");

Ich hatte File::Find absichtlich nicht verwendet, aber danke für den
Hinweis.

Davon ab: Ich bin bemüht, solche Konstrukte wie das obige zu
vermeiden. In drei Monaten wüsste ich nämlich nicht mehr, was diese
Zeile tut.

Mark

--
Im Falle eines Atomkriegs gehe ich in die Schweiz; dort findet alles zwanzig
Jahre später statt als anderswo
[Albert Einstein]

Re: Nicht-rekursive Rekursion

am 20.09.2006 14:08:39 von Mark Neis

J. Strübig schrieb:

>ein -w und use strict hätten dir die zmuindest veraltete Schreibweise
>die du verwendest bemängelt.

Ack. Basiert übrigens auf Code aus einem O'Reilly, Baujahr 2000.


>>
>> sub processdir {
>> $dir = @_[0];
>> $level = @_[1];

>my($dir, $level) = @_;

Ok, gekauft.



>> opendir(DIR, $dir) or die "Can't open $dir";
>
>Den Grund für einen Fehler kannst du dir auch noch mitteilen lassen

Ja, das zumindest ist mir bekannt.



>> @files = readdir(DIR);
>> closedir DIR;
>>
>> foreach $f (@files) {
>
>Wie schon erwähnt, erhälst du hier nur die Datennamen ohne Verzeichniss,
>d.h. $f sollte besser sowas sein:
>
>foreach (@files) {
>my $f = "$dir/$_";

Ok.


>>
>> $basename = $f;
>> $basename =~ s/\/$//;
>> $basename =~ s/^.*\///;
>
>Das liesse sich leichter ermitteln:
>
>$f =~ /.*\/(.*)$/g;
>my $basename = $1;

Wenn ich deine Regexp richtig verstehe, trifft die's nicht ganz.
Korrigier mich bitte, wenn ich falsch liege:

..*\/ ist gierig und matcht daher alles bis zum am weitesten rechts
stehenden Slash. $1 enthält nach dem Match den eingeklammerten
Ausdruck. Sollte es sich bei $f um einen String handeln, der als
letztes Zeichen einen Slash hat (Verzeichnis), ist $1 anschließend
leer, oder? In dem Fall bräuchte ich aber trotzdem den
Verzeichnisnamen. Das war der Grund, weshalb ich zunächst einen evt.
vorhandenen Slash am Stringende wegnehme.


>Wobei das Überflüssig ist, da du ja in @files bereits den "basenamen"
>hast.

Ok, andere Frage.

Mark

--
Im Falle eines Atomkriegs gehe ich in die Schweiz; dort findet alles zwanzig
Jahre später statt als anderswo
[Albert Einstein]

Re: Nicht-rekursive Rekursion

am 20.09.2006 14:38:02 von Daniel Fischer

Mark Neis!

> Davon ab: Ich bin bemüht, solche Konstrukte wie das obige zu
> vermeiden. In drei Monaten wüsste ich nämlich nicht mehr, was diese
> Zeile tut.

Was für Konstrukte? Das ist ein ganz normaler find-Aufruf (die sehen
alle so aus, wenn man Optionen setzen will) und der Body der anonymen sub
ist auch nicht sehr kompliziert.

Wenn Du File::Find nicht benutzen willst, gibt es auch noch File::Walker
und File::DirWalk. Die musst Du freilich selbst nachinstallieren.


Gruß
Daniel

Re: Nicht-rekursive Rekursion

am 20.09.2006 22:07:51 von Frank Seitz

Mark Neis wrote:
> Daniel Fischer schrieb:
>>
>>Also ungefähr sowas?
>>
>>use File::Find;
>>find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");
>
> Ich hatte File::Find absichtlich nicht verwendet, aber danke für den
> Hinweis.
>
> Davon ab: Ich bin bemüht, solche Konstrukte wie das obige zu
> vermeiden. In drei Monaten wüsste ich nämlich nicht mehr, was diese
> Zeile tut.

Dann fehlt Dir noch Übung in Perl.
File::Find hat sicher nicht die hübscheste API, aber das,
was da steht, ist - ausgenommen y|/||-1 - gewöhnlicher Perlcode,
der Dir überall begegnen kann.

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: Nicht-rekursive Rekursion

am 21.09.2006 10:20:43 von Peter Arnhold

Frank Seitz schrieb:
> Mark Neis wrote:
>> Daniel Fischer schrieb:
>>> Also ungefähr sowas?
>>>
>>> use File::Find;
>>> find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");
>> Ich hatte File::Find absichtlich nicht verwendet, aber danke für den
>> Hinweis.
>>
>> Davon ab: Ich bin bemüht, solche Konstrukte wie das obige zu
>> vermeiden. In drei Monaten wüsste ich nämlich nicht mehr, was diese
>> Zeile tut.
>
> Dann fehlt Dir noch Übung in Perl.
> File::Find hat sicher nicht die hübscheste API, aber das,
> was da steht, ist - ausgenommen y|/||-1 - gewöhnlicher Perlcode,
> der Dir überall begegnen kann.
>
> Grüße
> Frank

"Kann" ja, "muss" nicht. Mark hat nur dokumentiert, dass er nicht dazu
beiträgt, dass man solchem Code begegnet.

Über die Regeln zur Gestaltung von Code kann man sicher geteilter Meinung
sein. Das obige Beispiel ist zwar von der Syntax her gewöhnlicher Perlcode,
schön kompakt, eigentlich ganz nett, richtig perlig.

Dennoch würde es, wenn man die Maßstäbe aus Damian Conways "Perl Best
Practices" ansetzt, ein ziemlich grausiges Beispiel abgeben. Ich stimme Mark
durchaus zu, was das Vermeiden solcher Konstrukte angeht. Es sei denn, man
legt es darauf an, wie z. B. in Signaturen. Dann ist der Sinn aber auch ein
anderer als wartbarer Programmcode oder gar ein Beitrag in einer NG, in dem
man jemanden bei einer Sache helfen will, mit der er ohnehin schon ein
Problem hat.

Gruß,
Peter

Re: Nicht-rekursive Rekursion

am 21.09.2006 13:09:31 von Frank Seitz

Peter Arnhold wrote:
> Frank Seitz schrieb:
>>Mark Neis wrote:
>>>Daniel Fischer schrieb:
>>>
>>>>Also ungefähr sowas?
>>>>
>>>>use File::Find;
>>>>find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");
>>>
>>>Ich hatte File::Find absichtlich nicht verwendet, aber danke für den
>>>Hinweis.
>>>
>>>Davon ab: Ich bin bemüht, solche Konstrukte wie das obige zu
>>>vermeiden. In drei Monaten wüsste ich nämlich nicht mehr, was diese
>>>Zeile tut.
>>
>>Dann fehlt Dir noch Übung in Perl.
>>File::Find hat sicher nicht die hübscheste API, aber das,
>>was da steht, ist - ausgenommen y|/||-1 - gewöhnlicher Perlcode,
>>der Dir überall begegnen kann.
>
> "Kann" ja, "muss" nicht. Mark hat nur dokumentiert, dass er nicht dazu
> beiträgt, dass man solchem Code begegnet.
>
> Über die Regeln zur Gestaltung von Code kann man sicher geteilter Meinung
> sein. Das obige Beispiel ist zwar von der Syntax her gewöhnlicher Perlcode,
> schön kompakt, eigentlich ganz nett, richtig perlig.
>
> Dennoch würde es, wenn man die Maßstäbe aus Damian Conways "Perl Best
> Practices" ansetzt, ein ziemlich grausiges Beispiel abgeben.

Inwiefern?

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: Nicht-rekursive Rekursion

am 21.09.2006 14:12:00 von hjp-usenet2

On 2006-09-21 08:20, Peter Arnhold wrote:
> Frank Seitz schrieb:
>> Mark Neis wrote:
>>> Daniel Fischer schrieb:
>>>> use File::Find;
>>>> find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");
>>> Ich hatte File::Find absichtlich nicht verwendet, aber danke für den
>>> Hinweis.
>>>
>>> Davon ab: Ich bin bemüht, solche Konstrukte wie das obige zu
>>> vermeiden. In drei Monaten wüsste ich nämlich nicht mehr, was diese
>>> Zeile tut.
[...]
> Über die Regeln zur Gestaltung von Code kann man sicher geteilter Meinung
> sein. Das obige Beispiel ist zwar von der Syntax her gewöhnlicher Perlcode,
> schön kompakt, eigentlich ganz nett, richtig perlig.
>
> Dennoch würde es, wenn man die Maßstäbe aus Damian Conways "Perl Best
> Practices" ansetzt, ein ziemlich grausiges Beispiel abgeben. Ich stimme Mark
> durchaus zu, was das Vermeiden solcher Konstrukte angeht.

Was genau meint Ihr mit "solchen Konstrukten"?

Auf den ersten Blick fällt mir da nur der Mangel an Whitespace auf, aber
das würde ich nicht als "Konstrukt" bezeichnen (Einen Strichpunkt kann man
übrigens noch einsparen, wenn das ein Golfspiel sein sollte).

Auf den zweiten merke ich, dass ich nicht verstehe, warum das y|/||
offensichtlich $_ nicht modifiziert. Also muss ich nachlesen:

| If the REPLACEMENTLIST is
| empty, the SEARCHLIST is replicated. This latter is useful for
| counting characters in a class or for squashing character
| sequences in a class.

Aha. Wieder was gelernt. Trotzdem würde ich statt y|/|| lieber tr|/|/|
schreiben.

Sonst sehe ich da keine zweifelhaften Konstrukte. Anonymous Subs sind
genz normales und idiomatisches Perl, Hashrefs ebenfalls (auch als
Parameter für Funktionen), daran ist nichts Böses. Die Vermeidung dieser
Konstrukte würde den Code im Gegenteil schwerer lesbar machen.

hp


--
_ | 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: Nicht-rekursive Rekursion

am 21.09.2006 14:22:09 von Ch Lamprecht

Frank Seitz schrieb:
> Peter Arnhold wrote:
>
>>Frank Seitz schrieb:
>>
>>>Mark Neis wrote:
>>>
>>>>Daniel Fischer schrieb:
>>>>
>>>>
>>>>>Also ungefähr sowas?
>>>>>
>>>>>use File::Find;
>>>>>find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");
>>>>
>>>>Ich hatte File::Find absichtlich nicht verwendet, aber danke für den
>>>>Hinweis.
>>>>
>>>>Davon ab: Ich bin bemüht, solche Konstrukte wie das obige zu
>>>>vermeiden. In drei Monaten wüsste ich nämlich nicht mehr, was diese
>>>>Zeile tut.
>>>
>>>Dann fehlt Dir noch Übung in Perl.
>>>File::Find hat sicher nicht die hübscheste API, aber das,
>>>was da steht, ist - ausgenommen y|/||-1 - gewöhnlicher Perlcode,
>>>der Dir überall begegnen kann.
>>
>>"Kann" ja, "muss" nicht. Mark hat nur dokumentiert, dass er nicht dazu
>>beiträgt, dass man solchem Code begegnet.
>>
>>Über die Regeln zur Gestaltung von Code kann man sicher geteilter Meinung
>>sein. Das obige Beispiel ist zwar von der Syntax her gewöhnlicher Perlcode,
>>schön kompakt, eigentlich ganz nett, richtig perlig.
>>
>>Dennoch würde es, wenn man die Maßstäbe aus Damian Conways "Perl Best
>>Practices" ansetzt, ein ziemlich grausiges Beispiel abgeben.
>
>
> Inwiefern?


find({wanted=>sub{printf "(%d) %s\n", y{/}{} - 1, $_;},no_chdir=>1},".");
^^^^^^^^^^^
Das versteht man vielleicht schneller - aber grausig?

Oder umgebrochen (nicht explizit aus PBP):

find({wanted => sub{printf "(%d) %s\n",y{/}{} - 1, $_;},
no_chdir => 1},
".");

Christoph
--

perl -e "print scalar reverse q/ed.enilno@ergn.l.hc/"

Re: Nicht-rekursive Rekursion

am 22.09.2006 11:23:44 von Peter Arnhold

Frank Seitz schrieb:
> Peter Arnhold wrote:
>> Frank Seitz schrieb:
>>> Mark Neis wrote:
>>>> Daniel Fischer schrieb:
>>>>
>>>>> Also ungefähr sowas?
>>>>>
>>>>> use File::Find;
>>>>> find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");
>>>> Ich hatte File::Find absichtlich nicht verwendet, aber danke für den
>>>> Hinweis.
>>>>
>>>> Davon ab: Ich bin bemüht, solche Konstrukte wie das obige zu
>>>> vermeiden. In drei Monaten wüsste ich nämlich nicht mehr, was diese
>>>> Zeile tut.
>>> Dann fehlt Dir noch Übung in Perl.
>>> File::Find hat sicher nicht die hübscheste API, aber das,
>>> was da steht, ist - ausgenommen y|/||-1 - gewöhnlicher Perlcode,
>>> der Dir überall begegnen kann.
>> "Kann" ja, "muss" nicht. Mark hat nur dokumentiert, dass er nicht dazu
>> beiträgt, dass man solchem Code begegnet.
>>
>> Über die Regeln zur Gestaltung von Code kann man sicher geteilter Meinung
>> sein. Das obige Beispiel ist zwar von der Syntax her gewöhnlicher Perlcode,
>> schön kompakt, eigentlich ganz nett, richtig perlig.
>>
>> Dennoch würde es, wenn man die Maßstäbe aus Damian Conways "Perl Best
>> Practices" ansetzt, ein ziemlich grausiges Beispiel abgeben.
>
> Inwiefern?

Z. B.
- (keine) Whitespaces,
- das Verwenden von "y",
- das Verwenden von "|" als Delimiter,
- das Semikolon innerhalb einer (endlosen) Zeile,
- das Verwenden von $_, von dem man an dieser Stelle wissen muss, dass es
durch find gesetzt wird und noch dazu nur dann das Gewollte enthält,
wenn man no_chdir=>1 setzt.

Und da der Anwender das pure printen nach STDOUT nicht brauchen wird, muss
die sub sowieso aufgebohrt werden, was den Rahmen dieser Zeile völlig
sprengen würde.
Da zeigt sich noch ein weiterer Mangel. Sollte man die anonyme sub in eine
benannte auslagern, ist das Stück Code auch schnell per cut and paste an
eine andere Stelle bewegt. Wenn das $_ dann unkommentiert überlebt, weiß man
gleich gar nicht mehr, was sich dahinter verbirgt. Eine kommentierte
Zuweisung an eine Variable mit sinnvollem Namen löst das Problem. Kommentare
innerhalb eines Einzeilers machen sich aber schlecht in Perl.

Das Buch hat 19 Kapitel, über 500 Seiten. Nicht nur ein bloßes Aufzählen,
wie oben, sondern mit ausführlichen Erläuterungen, *warum* man was wie
handhaben sollte.

Btw, ich finde die Zeile gelungen. Aber ich würde sie nicht in Code
einsetzen, den andere Leute nutzen und ich würde sie nicht jemandem als
Hilfe anbieten, der so was wie "$dir = @_[0];" schreibt.

Gruß,
Peter

Re: Nicht-rekursive Rekursion

am 22.09.2006 15:23:52 von hjp-usenet2

On 2006-09-22 09:23, Peter Arnhold wrote:
> Frank Seitz schrieb:
>> Peter Arnhold wrote:
>>> Frank Seitz schrieb:
>>>> Mark Neis wrote:
>>>>> Daniel Fischer schrieb:
>>>>>> use File::Find;
>>>>>> find({wanted=>sub{printf "(%d) %s\n",y|/||-1,$_;},no_chdir=>1},".");
[...]
>>> Dennoch würde es, wenn man die Maßstäbe aus Damian Conways "Perl Best
>>> Practices" ansetzt, ein ziemlich grausiges Beispiel abgeben.
>>
>> Inwiefern?
>
> Z. B.
> - (keine) Whitespaces,

Yup.

> - das Verwenden von "y",

Stört Dich nur an der Schreibweise "y" statt "tr", oder die Verwendung
des Operators zum zählen?

> - das Verwenden von "|" als Delimiter,

Daran habe ich wieder überhaupt nichts auszusetzen, im Gegenteil. "|"
ist bei mir die zweite Wahl (nach "/") für Delimiter, wegen der
optischen Ähnlichkeit mit "/". Bei kompliziertern Ausdrücken verwende
ich gerne {}, aber bei einem Ausdruck von einem Zeichen Länge würde das
die Lesbarkeit nicht verbessern. "|/||" ist jedenfalls lesbarer als
"/\///" und "{/}{}" liegt irgendwo dazwischen.

> - das Semikolon innerhalb einer (endlosen) Zeile,

Das Semicolon kann man weglassen. Es stört aber IMHO nicht.
Endlos finde ich die Zeile nicht. Auch mit ausreichend Whitespace ist
sie deutlich unter 80 Zeichen.

> - das Verwenden von $_, von dem man an dieser Stelle wissen muss, dass es
> durch find gesetzt wird und noch dazu nur dann das Gewollte enthält,
> wenn man no_chdir=>1 setzt.

Das steht in der Doku von File::Find. Die Verwendung von $_ für "das
worum es gerade geht" ist extrem idiomatisch für Perl. Das ist
konsistent zur Verwendung in map und grep (und in weiterem Sinne auch zu
for(@LIST) und while(<>)). Statt $_ hätte man auch $File::Find::name
verwenden können, aber das wäre IMHO überhaupt nicht lesbarer gewesen.
Auch da hätte der mit File::Find nicht Vertraute erst nachlesen müssen,
ob das der gesamte Pfad oder nur der Filename ist.


> Und da der Anwender das pure printen nach STDOUT nicht brauchen wird, muss
> die sub sowieso aufgebohrt werden, was den Rahmen dieser Zeile völlig
> sprengen würde.

Nope, dann teilt man sie halt in mehrere Zeilen auf (würde ich hier auch
schon machen):

find(
{
wanted => sub{
my $level = tr|/|| - 1;
do_something_with($level, $_);
do_some_more_with($level, $_);
},
no_chdir => 1
},
"."
);

> Da zeigt sich noch ein weiterer Mangel. Sollte man die anonyme sub in eine
> benannte auslagern, ist das Stück Code auch schnell per cut and paste an
> eine andere Stelle bewegt. Wenn das $_ dann unkommentiert überlebt, weiß man
> gleich gar nicht mehr, was sich dahinter verbirgt.

$_ sollte man nicht global verwenden, ja. Aber wenn die
Callback-Funktion von find (oder map oder grep oder ...) so komplex
wird, dass man Teile davon in andere Funktionen auslagern muss, dann
wird man i.A. auch mehrere Variable und Parameter brauchen, so dass die
Zuordnung "Pfadname" == "das worum es gerade geht" nicht mehr klar ist
und man aus diesem Grund eine benannte Variable $path haben möchte. In
begrenztem Kontext auf $_ zu verzichten, nur weil es vielleicht sein
könnte, dass der Kontext irgendwann einmal wächst, halte ich nicht für
sinnvoll. (und ja, der Fall, dass ich in einer Schleife $_ durch
$irgendein_name ersetze, wenn mir die Schleife zu lang wird, kommt vor.
Darin sehe ich kein Problem)

> Eine kommentierte Zuweisung an eine Variable mit sinnvollem Namen löst
> das Problem. Kommentare innerhalb eines Einzeilers machen sich aber
> schlecht in Perl.

Wenn man innerhalb eines Einzeilers Kommentare braucht, hat man was
falsch gemacht. Nicht umsonst kann Newlines fast überall einfügen (sogar
innerhalb von Regexes).

> ich würde sie nicht jemandem als Hilfe anbieten, der so was wie
> "$dir = @_[0];" schreibt.

Zumindest nicht ohne längere Erläuterungen. Problem bei Leuten, die
"$dir = @_[0];" schreiben, ist allerdings, dass sie oft an Erläuterungen
nicht interessiert sind, sondern nur Kochrezepte haben wollen. Wenn es
funktioniert (und sei es nur zufällig), sind sie schon zufrieden.

hp


--
_ | 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: Nicht-rekursive Rekursion

am 22.09.2006 22:46:37 von Mark Neis

Frank Seitz schrieb:

>Mark Neis wrote:
>Dann fehlt Dir noch Übung in Perl.

Das ganz sicher. Ich spreche ein paar andere Sprachen fließend;
in Perl schreibe ich nur sehr selten etwas.


>File::Find hat sicher nicht die hübscheste API, aber das,
>was da steht, ist - ausgenommen y|/||-1 - gewöhnlicher Perlcode,
>der Dir überall begegnen kann.

Deswegen benutze ich Perl so selten ;)

Mark

--
Mir wurde neulich etwas von einem Job als Bildschirmschonertester
erzählt. Evtl. sollte ich mich ja mal bewerben.
[Jens Link in dasr]

Re: Nicht-rekursive Rekursion

am 22.09.2006 22:49:14 von Mark Neis

Peter Arnhold schrieb:

>Btw, ich finde die Zeile gelungen. Aber ich würde sie nicht in Code
>einsetzen, den andere Leute nutzen und ich würde sie nicht jemandem als
>Hilfe anbieten, der so was wie "$dir = @_[0];" schreibt.

Isch werds nie wieder tun, isch schwör!

*ymmd*,
Mark

--
Mir wurde neulich etwas von einem Job als Bildschirmschonertester
erzählt. Evtl. sollte ich mich ja mal bewerben.
[Jens Link in dasr]

Re: Nicht-rekursive Rekursion

am 22.09.2006 22:51:30 von Mark Neis

Peter J. Holzer schrieb:

>Peter Arnhold wrote:

>> ich würde sie nicht jemandem als Hilfe anbieten, der so was wie
>> "$dir = @_[0];" schreibt.
>
>Zumindest nicht ohne längere Erläuterungen. Problem bei Leuten, die
>"$dir = @_[0];" schreiben, ist allerdings, dass sie oft an Erläuterungen
>nicht interessiert sind, sondern nur Kochrezepte haben wollen. Wenn es
>funktioniert (und sei es nur zufällig), sind sie schon zufrieden.

Gut. Ich höre.

Dass ich in Perl ein Stümper bin, der sich seinen Code erst aus
Büchern Webseiten zusammensuchen muss, bedeutet nicht, dass ich
lernunwillig oder -fähig bin.

Mark

--
Mir wurde neulich etwas von einem Job als Bildschirmschonertester
erzählt. Evtl. sollte ich mich ja mal bewerben.
[Jens Link in dasr]

Re: Nicht-rekursive Rekursion

am 23.09.2006 10:02:20 von hjp-usenet2

On 2006-09-22 20:51, Mark Neis wrote:
> Peter J. Holzer schrieb:
>>Peter Arnhold wrote:
>>> ich würde sie nicht jemandem als Hilfe anbieten, der so was wie
>>> "$dir = @_[0];" schreibt.
>>
>>Zumindest nicht ohne längere Erläuterungen. Problem bei Leuten, die
>>"$dir = @_[0];" schreiben, ist allerdings, dass sie oft an Erläuterungen
>>nicht interessiert sind, sondern nur Kochrezepte haben wollen. Wenn es
>>funktioniert (und sei es nur zufällig), sind sie schon zufrieden.
>
> Gut. Ich höre.

Welche Erläuterung willst Du hören? Die zu dem find-Code oder die zu
@_[0]?

Zu ersterem ist in diesem Thread schon ziemlich viel geschrieben worden.
Wenn Dir da was noch nicht klar ist (was durchaus wahrscheinlich ist,
weil die Diskussion sich eher um Stilfragen gedreht hat und die
Funktionsweise eher nebenbei abgehandelt wurde), stell bitte konkrete
Fragen.

Zu letzterem: Die "Sigils" ("$", "@", "%", "&") kann man als Artikel
betrachten (tatsächlich kann man sie auch von der Variable, auf die sie
sich beziehen, trennen: »$ x = $ y« ist korrektes, wenn auch
ungewöhnliches Perl). $ drückt eine Einzahl aus, @ eine Mehrzahl.

Wenn Du also *ein* Element des Arrays _ haben willst, schreibst Du
$_[0]. Richtig wäre also $dir = $_[0].

@_[...] verwendet man, um *mehrere* Elemente eines Arrays zu wählen.
@_[0, 2, 7..9] würde etwa eine Liste bestehend aus dem 0ten, 2ten, 7ten,
8ten und 9ten Element liefern. Bei »$dir = @_[0]« bildest Du eine Liste
mit einem Element, von der Du dann das erste Element $dir zuweist. Das
Ergebnis ist zwar richtig, aber es liest sich ziemlich umständlich.

In Deinem Fall wolltest Du aber sogar zwei Elemente zuweisen:

$dir = @_[0];
$level = @_[1];

Das könntest Du als

($dir, $level) = @_[0, 1];

schreiben. Da das aber genau die ersten beiden Elemente von @_ sind,
lässt sich das zu

($dir, $level) = @_;

vereinfachen. Da diese Variablen aber nur in dieser Funktion sichtbar
sein sollten, solltest Du sie auch als lexikalische Variable
deklarieren:

my ($dir, $level) = @_;

Und damit sind wir bei der üblichen Form, Parameter in Perl lokalen
Variablen zuzuweisen, angelangt (Du solltest Dir übrigens die
Verwendung von »use strict« und »use warnings« angewöhnen).


> Dass ich in Perl ein Stümper bin, der sich seinen Code erst aus
> Büchern Webseiten zusammensuchen muss, bedeutet nicht, dass ich
> lernunwillig oder -fähig bin.

Sorry, falls das so rübergekommen ist, dass ich dass ich Dich der
Lernunwilligkeit bezichtige. Das war nur eine allgemeine Beobachtung
(ich habe auch absichtlich "oft" und nicht "meistens" oder gar "immer"
geschrieben).

hp


--
_ | 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: Nicht-rekursive Rekursion

am 23.09.2006 12:14:51 von Mark Neis

Peter J. Holzer schrieb:

>Mark Neis wrote:
>> Gut. Ich höre.
>
>Welche Erläuterung willst Du hören? Die zu dem find-Code oder die zu
>@_[0]?

Letzteres.


>Zu ersterem ist in diesem Thread schon ziemlich viel geschrieben worden.
>Wenn Dir da was noch nicht klar ist (was durchaus wahrscheinlich ist,
>weil die Diskussion sich eher um Stilfragen gedreht hat und die
>Funktionsweise eher nebenbei abgehandelt wurde), stell bitte konkrete
>Fragen.

Das muss ich mir erst mal ansehen. Ich bin ganz gut vertraut mit Shell
und C - ich hoffe, das ist nicht _wesentlich_ anders.


[Erklärung zu Sigils gesnippt]

Danke für die ausführliche Erläuterung.


> my ($dir, $level) = @_;
>
>Und damit sind wir bei der üblichen Form, Parameter in Perl lokalen
>Variablen zuzuweisen, angelangt

Ja, das hatte ich schon weiter oben im Thread verstanden und bereits
umgesetzt.


>(Du solltest Dir übrigens die
>Verwendung von »use strict« und »use warnings« angewöhnen).

Done. Ist eigentlich "perl -w" äquivalent zu "use warnings;"?

Grüße,
Mark

--
Chris: I hate the way flash slows browsers down
Steve: Try java, that slows it down much better
[bash.org]

Re: Nicht-rekursive Rekursion

am 23.09.2006 14:24:19 von hjp-usenet2

On 2006-09-23 10:14, Mark Neis wrote:
> Ist eigentlich "perl -w" äquivalent zu "use warnings;"?

Fast. -w wirkt global (also auch in mit use bzw. require eingebundenen
Modulen), »use warnings« hingegen nur im lexikalischen
Sichtbarkeitsbereich. In der Praxis sollte das wenig Unterschied machen,
»use warnings« gilt als sauberer.

Die Einführung des »warnings« Pragmas zusätzlich zum Commandline-Switch
brachte allerdings den Vorteil, dass man damit nicht nur Warnungen ein-
sondern auch ausschalten kann. Z.B. kommt es bei mir relativ häufig vor,
dass ich in einem bestimmten Code-Block undefinierte Werte als
Leerstring behandeln will. Dann schreibe ich einfach

{
no warnings 'uninitialized';
...
}

und kann dann innerhalb des Blocks einfach $x verwenden, und muss nicht
(defined $x ? $x : '') schreiben. Ebenso kann ich z.B. in einer
Funktion, die sich tausendmal selbst aufrufen soll, die "deep recursion"
Warnung abschalten.

hp


--
_ | 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: Nicht-rekursive Rekursion

am 25.09.2006 10:54:26 von Daniel Fischer

Peter J. Holzer!

> Auf den ersten Blick fällt mir da nur der Mangel an Whitespace auf,
> aber das würde ich nicht als "Konstrukt" bezeichnen (Einen Strichpunkt
> kann man übrigens noch einsparen, wenn das ein Golfspiel sein sollte).

Sollte kein Golfspiel sein. In einem echten Programm würde ich auch mehr
Whitespace verwenden. Im Moment arbeite ich mit einem Display, das
1600x1200 Pixel darstellen kann. Früher war das noch ganz gut weniger.
Daher die Neigung, in E-Mail und News Code zu komprimieren.


Gruß
Daniel

Re: Nicht-rekursive Rekursion

am 26.09.2006 09:04:55 von Uwe.Kloss

Peter J. Holzer wrote:

> Z.B. kommt es bei mir relativ häufig vor,
> dass ich in einem bestimmten Code-Block undefinierte Werte als
> Leerstring behandeln will. Dann schreibe ich einfach
>
> {
> no warnings 'uninitialized';
> ...
> }
>
> und kann dann innerhalb des Blocks einfach $x verwenden, und muss nicht
> (defined $x ? $x : '') schreiben. Ebenso kann ich z.B. in einer
> Funktion, die sich tausendmal selbst aufrufen soll, die "deep recursion"
> Warnung abschalten.

Sorry, wenn aus dem Zusammenhang gerissen,
aber auf die Geschichte bin ich vor kurzem reingefallen:

Der 'undefined' in $x kommt ja sicherlich ;-) nicht
daher, daß Du $x einfach nur benutzt ohne es zu definieren
sondern aus einer Zuweisung!?

Spricht in der Situation was gegen "$x" bzw. "$y"?
Also $y ist 'undefined' und Du schreibst:
$x = "$y";
statt
$x = $y;

Das war damals meine 'quick and dirty' - Abhilfe.
Ist deutlich kürzer als die beiden Versionen weiter oben.

Grüße,
Uwe

Re: Nicht-rekursive Rekursion

am 26.09.2006 12:10:54 von hjp-usenet2

On 2006-09-26 07:04, Uwe Kloß wrote:
> Peter J. Holzer wrote:
>> Z.B. kommt es bei mir relativ häufig vor,
>> dass ich in einem bestimmten Code-Block undefinierte Werte als
>> Leerstring behandeln will. Dann schreibe ich einfach
>>
>> {
>> no warnings 'uninitialized';
>> ...
>> }
>>
>> und kann dann innerhalb des Blocks einfach $x verwenden, und muss nicht
>> (defined $x ? $x : '') schreiben.
>
> Sorry, wenn aus dem Zusammenhang gerissen,
> aber auf die Geschichte bin ich vor kurzem reingefallen:
>
> Der 'undefined' in $x kommt ja sicherlich ;-) nicht
> daher, daß Du $x einfach nur benutzt ohne es zu definieren
> sondern aus einer Zuweisung!?

Nein, es kommt daher, dass ich es irgendwo verwende, wo man undefinierte
Werte normalerweise nicht verwenden will, z.B. in einem print, einer
String-Konkatenation oder einem arithmetischen Ausdruck.


> Spricht in der Situation was gegen "$x" bzw. "$y"?
> Also $y ist 'undefined' und Du schreibst:
> $x = "$y";
> statt
> $x = $y;

Ja. Es tut genau das Gegenteil von dem, was Du erwartest.

$x = $y ist ok. $x ist danach genauso undef wie $y.

$x = "$y" ergibt die Warnung

"Use of uninitialized value in string at ..."

Typischerweise passiert das bei bei der Ausgabe von irgendwelchen
Tabellen. Sowas in der Art:

for my $r (@row_keys) {
print "\n";
for my $c (@col_keys) {
print "", $data->{$r}{$c}, "";
}
print "\n";
}

In dem Fall weiß ich u.U., dass nicht alle Kombinationen von @row_keys
und @col_keys tatsächlich in $data vorkommen, und möchte die leeren
Felder einfach leer lassen. Das dokumentiere ich, indem ich schreibe:

for my $r (@row_keys) {
print "\n";
for my $c (@col_keys) {
no warnings 'uninitialized';
print "", $data->{$r}{$c}, "";
}
print "\n";
}

Das erscheint mir lesbarer als

for my $r (@row_keys) {
print "\n";
for my $c (@col_keys) {
print "", ($data->{$r}{$c} ? $data->{$r}{$c} : ''), "";
}
print "\n";
}

In perl 5.9 gibt es den // (gesprochen "err", deutschsprachige dürfen
auch "öder" sagen :-)) Operator. Damit könnte man

for my $r (@row_keys) {
print "\n";
for my $c (@col_keys) {
print "", $data->{$r}{$c} // '', "";
}
print "\n";
}

schreiben, was auch recht lesbar ist, aber erstens muss ich mich
momentan noch auch perl 5.8 beschränken (ich bin schon froh, dass ich
auf perl 5.005 keine Rücksicht mehr nehmen muss) und zweitens handelt
es sich häufig um mehrere Ausdrücke und da ist einmal ein "In diesem
Block sind undefined values zu erwarten" übersichtlicher als wenn das in
jedem einzelnen Ausdruck drinsteht.


> Das war damals meine 'quick and dirty' - Abhilfe.
> Ist deutlich kürzer als die beiden Versionen weiter oben.

Was nützt die Kürze, wenn es nicht funktioniert?

hp


--
_ | 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