Perl: Multidimensionales Array erweitern

Hallöchen,
ich habe mich mit meinem Versuch, ein 2D-Array um einen Vektor zu erweitern, ziemlich in die Nesseln gesetzt.
So sieht es aus:

Variable @dataArray sei global.

sub addLine(@)
{
my @newLine=@\_;
push (@dataArray, @newLine)
}

Zu dumm nur, dass dies einzeln die String-Elemente aus newLine auf mein Array ballert und nicht ein einzelnes Array-Objekt anheftet :frowning:

Muß ich den Umweg gehen, mein Array mit Objekten einer manuell zu erstellenden „myArray“ Klasse zu befüllen oder geht das irgendwie „direkter“?

Gruss,
Michael

Hallo,

ich habe mich mit meinem Versuch, ein 2D-Array um einen Vektor
zu erweitern, ziemlich in die Nesseln gesetzt.
So sieht es aus:

Variable @dataArray sei global.

sub addLine(@)
{
my @newLine=@_;
push (@dataArray, @newLine)
}

Zu dumm nur, dass dies einzeln die String-Elemente aus newLine
auf mein Array ballert und nicht ein einzelnes Array-Objekt
anheftet :frowning:

Das ist sogenanntes „auto list flattening“. Du kannst es umgehen, indem du eine Refernz auf ein Array verwendest:

sub addLine(@)
{
 my @newLine=@\_;
 push (@dataArray, \@newLine)
}

Ich empfehle dir zum lesen perldoc perlreftut http://perldoc.perl.org/perlreftut.html und perllol http://perldoc.perl.org/perllol.html (lol ist hier „lists of lists“).

HTH,
Moritz

Das ist sogenanntes „auto list flattening“. Du kannst es
umgehen, indem du eine Refernz auf ein Array verwendest:

Danke - mit den Referenzen bei perl stehe ich im Moment noch auf Kriegsfuß. Hoffe, die Dokus die Du empfiehlst helfen mir ^^

Kriegst ein Sternchen :smile:
Gruss,
Michael

Hallo,

Variable @dataArray sei global.

sub addLine(@)
{
my @newLine=@_;
push (@dataArray, @newLine)
}

Don’t ever do that! (addLine(@) - mit Prototyping)

Zu dumm nur, dass dies einzeln die String-Elemente aus newLine
auf mein Array ballert und nicht ein einzelnes Array-Objekt
anheftet :frowning:

Muß ich den Umweg gehen, mein Array mit Objekten einer manuell
zu erstellenden „myArray“ Klasse zu befüllen oder geht das
irgendwie „direkter“?

Direkter:

 sub addLine # do NOT use prototypes in Perl
{
 my ($r\_data, $r\_line) = @\_;
 push @$r\_data, $r\_line
}


 my @dataArray;
 my @newLine = qw' A B C D E F G H I J K L ';
 
 addLine \@dataArray, \@newLine;
 addLine \@dataArray, \@newLine;
 addLine \@dataArray, \@newLine;
 # @dataArrayist ein 2D-Feld mit 3 zeilen und je 12 Elementen
 
 # oder einfac mit \ ...
 push @dataArray, \@newLine;
 
 # oder auch: []
 push @dataArray, [@newLine];
 
 print scalar @dataArray, " Zeilen\n";

Grüße

CMБ

Hi Semjon.

Deine „do NOT use prototypes in Perl“-Anmerkung verstehe ich (noch) nicht (und Mike sicher auch nicht …).

Mikes Version mit Prototyp ohne Übergabe des zu ändernden Arrays:

Variable @dataArray sei global.

sub addLine(@)
{
my @newLine=@\_;
push (@dataArray, @newLine)
}

Don’t ever do that! (addLine(@) - mit Prototyping)

Dadurch, dass Mike in seiner addLine-Variante das
zu modifizierende Array nicht übergibt, sondern eine
globale Variable intern verwendet, sehe ich kein
Problem mit seinem Prototyp. Lediglich die Arbeit
mit einer globalen Variable ist nicht die beste Variante.

Deine Version ohne Prototypen und Übergabe des zu ändernden Arrays

sub addLine # do NOT use prototypes in Perl
{
 my ($r\_data, $r\_line) = @\_;
 push @$r\_data, $r\_line
}

Version mit Prototypen und Übergabe des zu ändernden Arrays
Das addLine könnte man auch mit Prototypen schreiben,
nämlich als

sub addLine(\@@) 
{
 my $r\_data = shift;
 push @$r\_data, @\_;
}

wie unter http://www.perl.com/pub/a/1999/11/sins.html#5
beschrieben.

Wo siehst Du das Problem mit Prototypen?

Gruß,
-Andreas.

Hallo Andreas,

Deine „do NOT use prototypes in Perl“-Anmerkung verstehe ich
(noch) nicht (und Mike sicher auch nicht …).

Wie Dein Link schon sagt -

 sub foo ($);

 foo(@x); 

"Perl takes the prototype as an indication that 
you would like the array automatically converted to a 
scalar, and passes the number of elements in the array 
to foo(). This will make it harder to debug, not easier.

Ein „Prototype“ in Perl, so wie er jetzt existiert,
hat nichts mit einem „formalen Parametertyp“ in C oder
C++ zu tun. Für Anfänger geht das fast immer schnell
in die Hose.

Mikes Version mit Prototyp ohne Übergabe des zu ändernden
Arrays:

Variable @dataArray sei global.
sub addLine(@)
{
my @newLine=@_;
push (@dataArray, @newLine)
}

Don’t ever do that! (addLine(@) - mit Prototyping)

Dadurch, dass Mike in seiner addLine-Variante das
zu modifizierende Array nicht übergibt, sondern eine
globale Variable intern verwendet, sehe ich kein
Problem mit seinem Prototyp. Lediglich die Arbeit
mit einer globalen Variable ist nicht die beste Variante.

Erstens das und Zweitens bedeutet in Perl die
Übergabe eines Arrays als Funktionsargument
immer die implizite Konvertierung dessen -
auf dem Laufzeitstack (in eine Liste - ‚flattened‘,
wie Moritz das schon erwähnte).

Die implizite Listenkonvertierung ist in den
meisten Fällen unerwünscht, im besten Falle
nur eine Verschwendung von Rechenzeit. Ausserdem
sollte man, wenn man Funktionen schreibt, bedenken,
ob sie auch mal mit einem Array von 10 Millionen
Elementen aufgerufen werden können und dann
hauptsächlich damit beschäftigt sind, etwas
zu tun, was sie gar nicht sollen.

Deine Version ohne Prototypen und Übergabe des zu ändernden
Arrays

sub addLine # do NOT use prototypes in Perl
{
my ($r_data, $r_line) = @_;
push @$r_data, $r_line
}

Version mit Prototypen und Übergabe des zu ändernden
Arrays

Das addLine könnte man auch mit Prototypen schreiben,
nämlich als

sub addLine(@@)
{
my $r_data = shift;
push @$r_data, @_;
}

wie unter http://www.perl.com/pub/a/1999/11/sins.html#5
beschrieben.
Wo siehst Du das Problem mit Prototypen?

Dein Beispiel kann ich ohne weiteres mit

 sub addLine(\@@)
 {
 my $r\_data = shift;
 push @$r\_data, @\_;
 }

 &addLine( @dataArray, \@newLine );
 &addLine( \@dataArray, @newLine );
 &addLine( @dataArray, @newLine );

aufrufen. Wüsstest Du aus dem Kopf, was
dann jeweils in der Funktion ankommt? Oder
was Perl meint, was es aus dem Ankommenden
macht?

Grüße

CMБ

1 Like

Hallo Semjon,

Deine „do NOT use prototypes in Perl“-Anmerkung verstehe ich
(noch) nicht (und Mike sicher auch nicht …).

sub foo ($);

"Perl takes the prototype as an indication that
you would like the array automatically converted to a
scalar, and passes the number of elements in the array
to foo().

Das Argument passt aber nicht auf unseren konkreten
Fall, wir erwarten keinen Skalar, sondern eine Liste
im Prototypen

sub foo (@);

In unserem Fall sehe ich bei dem Prototypen kein Problem,
wenn ich einen Skalar übergebe - er wird einfach als Liste
mit einem Element behandelt.

Die implizite Listenkonvertierung ist in den
meisten Fällen unerwünscht, im besten Falle
nur eine Verschwendung von Rechenzeit.

Da gebe ich Dir recht, nur ist das auch kein
Problem, welches durch die Verwendung von Prototypen
entsteht, sondern wie die Funktion an sich arbeitet.

Dein Beispiel kann ich ohne weiteres mit

sub addLine(@@)
{
my $r_data = shift;
push @$r_data, @_;
}

&addLine( @dataArray, @newLine );
&addLine( @dataArray, @newLine );
&addLine( @dataArray, @newLine );

aufrufen.

Wüsstest Du aus dem Kopf, was dann jeweils in der Funktion
ankommt? Oder was Perl meint, was es aus dem Ankommenden macht?

Nun, bei den Perl-Prototypen muss man vorher genau(!) die Doku
lesen, um die Zeile

sub addLine(\@@)

richtig zu verstehen, siehe http://www.perl.com/doc/manual/html/pod/perlsub.html….

Erkenntnisse:

(1) Bedeutung des Prototyps @
bedeutet demnach, dass die Funktion als erstes Argument ein echtes Array
(der Backslash weist auf das literale Zeichen „@“ hin) erwartet, welches
aber in der Funktionsargumentliste @_ von Perl als Arrayreferenz(!) abgelegt
wird.

(2) Bedeutung des Prototyps @
bedeutet, dass alle restlichen Argumente wie sie sind in die Argumentliste
wandern.

(3) Prototyp-Deaktivierung
„Old-School-Funktionsaufrufe“ mit führendem „&“ hebeln die Prototypprüfung aus.
Insofern sind Deine Beispiele der Form

&addLine( @dataArray, @newLine );

für das Testen von Prototypen wenig hilfreich.

Mit dem folgenden Wissen gewappnet, lassen sich Deine 3 korrigierten Versionen
kommentieren

Bsp. 1:

addLine( @dataArray, \@newLine );

Das nach (1) erwartete Array wird korrekt übergeben und
ihm eine Arrayreferenz angehängt. Wohl nicht das, was eigentlich
bezweckt war.

Bsp. 2:

addLine( \@dataArray, @newLine );

Ist ein Fehler, da nach (1) als erstes Argument
ein echtes Array erwartet wird. Perl erkennt das
natürlich auch und meckert:

Type of arg 1 to main::addLine must be array (not reference constructor)

Hier hat der Prototyp seine Funktion also voll und ganz erfüllt, indem er einen
Programmierfehler mit einem sofortigen, klaren Programmabruch quittiert - ein
Fehler weniger.

Bsp. 3:

addLine( @dataArray, @newLine );

Das erwartete Array im ersten Argument wird übergeben und
danach alle Folgeargumente hinten angehängt. Also das, was
Mike eigentlich wollte.

Alles in allem halte ich Perl-Prototypen für eine feine Sache.
Ihre Einführung wurde von den schlauen Perl-Entwicklern aus gutem
Grund gemacht.

Gruß,
-Andreas.

Don’t ever do that! (addLine(@) - mit Prototyping)

Och, eigentlich ist das ganz fein.
Dadurch kann ich sowas machen: addLine (@A, $X->{A},@{$Y},$Z, @B) … stimmt schon, man erwartet vielleicht was Anderes, aber es spart ne Menge Programmieraufwand :wink:

Ja, man kann mit Prototypes viel falsch machen, durfte ich auch schon erleben.
Dennoch finde ich sie ganz hilfreich, richtig eingesetzt und versuche eigentlich, sie dort anzuwenden, wo sie Sinn machen.
Im Übrigen empfinde ich das Array-an-String-übergeben als eines der geringsten Probleme, die ich mit perl habe :wink:

Gruss,
Michael