Schrift
[thread]11291[/thread]

Rückgabe mehrerer Hashes geht nicht (Seite 2)

Leser: 1


<< |< 1 2 3 >| >> 23 Einträge, 3 Seiten
Gast Gast
 2008-02-13 21:18
#105854 #105854
Die Möglichkeiten gibt es:
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/perl

use strict;
use warnings;

sub return_as_ref
{
  my ( %A, %B, %C );
  $A{1} = "Hugo";
  $B{1} = "Schmitt";
  $C{1} = "Vorsitzender";
  my $Count = 1;
  return (\%A,\%B,\%C,$Count);
}

sub get_as_ref
{
 my ($ref_A, $ref_B, $ref_C)=@_;
  $ref_A->{1} = "Hugo";
  $ref_B->{1} = "Schmitt";
  $ref_C->{1} = "Vorsitzender";
  my $Count = 1;
  return $Count;
}

my $Anzahl;
my (%A, %B, %C);
my ($ref_A,$ref_B,$ref_C);

($rA,$rB,$rC,$Anzahl)=return_as_ref();
print "$ref_A->{1} $ref_B->{1} $ref_C->{1}\n";

# derefernzieren:
%A=%{$ref_A};
%B=%{$ref_B};
%C=%{$ref_C};
print "$A{1} $B{1} $C{1}\n";


%A=(); %B=(); %C=();
$Anzahl=get_as_ref(\%A, \%B, \%C);
print "$A{1} $B{1} $C{1}\n";
KurtZ
 2008-02-13 22:09
#105855 #105855
User since
2007-12-13
411 Artikel
BenutzerIn
[default_avatar]
SchaubFD+2008-02-13 13:40:07--
Das ist dann aber eine Lösung die nur über Referenzen läuft, nicht über einen Hash oder ? Ich frage mich immer noch, warum muss man diese unterschiedlichen Typen nutzen, um auf einen Hash zugreifen zu können?


Deine Frage ist berechtigt, und da muss man weiter ausholen. es betrifft nicht nur Parameterübergaben sondern hängt mit der Besonderheit von Perl bezüglich verschachtelten Strukturen zusammen, die nur mit Referenzen möglich sind.

Generell gilt, dass man Listen von Skalaren Elementweise aufeinander abbilden kann.
Code (perl): (dl )
( $a, $b, $c ) = ( 1, 2, 3 ) ;# => $a=1, $b=2 usw.

trägt man aber rechtsseitig ein Array oder Hash ein, wird es alles zu einer eindimensionalen Liste abgeflacht (Flattening), also:

Code (perl): (dl )
1
2
3
4
5
6
7
@x = ( x1, x2, x3 ); 
%y=( y1 => "Y1 );

( $a, $b, $c ) = ( %y, $x, 1, 2, 3 );   

#                   = ( y1, Y1, x1, x2, x3, 1, 2, 3) 
#                   => $a == "y1",  $b == "Y1",  $c == "x1"  


Linksseitig wirkt der erste eingetragenen Array oder Hash wie ein Staubsauger, der alle folgenden Skalare aufnimmt

Code (perl): (dl )
1
2
3
($a,$b,@X,$c) =  (%y,$x,1,2,3);   
#              => @X = (x1,x2,x3,1,2,3) , $a = "y1",   $b = "Y1", 
#              => insbesondere $c = undef  


wenn du also schreibst:
Code (perl): (dl )
( @X ) = ( @x )


dann klappt deine Zuweisung, die Inhalte von @x werden 1zu1 auf @X umkopiert

Hingegen
Code (perl): (dl )
( @X , @Y ) = ( @x , @y ) 


liefert aber nur @X == ( @x , @y ) und @Y == ()

Referenzen hingegen werden wie Skalare behandelt, d.h. kein Aufsaugen!
Code (perl): (dl )
( $X_ref , $Y_ref ) = ( \@x , \@y ) 

Es funktioniert und hat den entscheidenden Unterschied dass hier keine einzelnen Arrayelemente umkopiert werden, sondern dass es sich auf beiden Seiten um die gleiche Referenz des selben Arrays handelt.

also
Code (perl): (dl )
1
2
3
$X_ref->[0] = "X" ;
# gilt genau dann wenn  
$x[0] = "X"  


Das ist ein entscheidender Vorteil, wenn man die übergebenen Arrays/Hashs direkt manipulieren will, aber auch das eventuell große Datenmengen nicht unnötig herumumkopiert werden müssen.

Was dich (wie auch mich) wohl nervt ist das du fortan immer den Pfeil mitschreiben musst um zu dereferenzieren.

Je nachdem ob du auf ein Alias oder eine Kopie zugreifen willst kannst du auch wieder derefrenzieren

Code (perl): (dl )
1
2
@X = @$X_ref   ; kopiert die Werte nach @X um
*X = $X_ref         ; legt ein Alias an, also  \@X==\$X_ref   


das letzte Konstrukt ist ein Typglob und ist mit Vorsicht zu genießen, weil es nur mit Packagevariablen funktioniert, dafür aber recht stringent, du kannst nämlich schreiben

Code (perl): (dl )
( *X , *Y , $A )=( \@x , \@y , $a)  


Weil du damit aber keine lexikalischen (my) Variablen aliasen kannst wird dir das aber heutzutage kaum jmd empfehlen.... dafür gibts in Perl5 leider keinen eingebauten Mechanismus, einziger Ausweg wäre da, das Modul CPAN:Data::Alias dafür zusätzlich zu nutzen.

Als die moderneren my-Variablen eingeführt wurden, wurde leider versäumt deren Aliasing direkt mit einzubauen.

Bedenke bitte das es in anderen Sprachen auch nur Referenzen übergeben werden, allerdings implizit!

In Javascript z.B. eine Referenz auf ein Array-Objekt. Umkopieren oder Flattening musst du da auch (mit Ausnahmen) explizit fordern. In Perl ist es halt defaultmäßig eingebaut, und muss daher im Bedarfsfall explizit verhindert werden.

Das hat Vor- und Nachteile, Perl erlaubt z.B. Konstrukte wie

Code (perl): (dl )
( $a , $b , $c , @X ) = ( @X ) 
d.h das Array @X wird um die ersten 3 Elemente gekürzt und diese auf $a-$c kopiert.

oder

Code (perl): (dl )
%A = ( %A, %B, %C )  
d.h. die Hashes werden zusammen auf %A kopiert!
Dabei werden mehrfach vorkommende Keys überschrieben werden.
(%B dominiert %A, %C dominiert alle anderen, usw)

SchaubFD+2008-02-13 13:40:07--
Auch wenn meine Fragen nervig sind, sind Übergaben von Hashes nur über Referenzen möglich?


Ein einzelnes Hash kannst du wie oben gezeigt per Einzelwerte umkopieren.

Bei mehreren kommst du an Referenzen nicht vorbei, ein zusätzlicher Backslash wird dich auch kaum umbringen.

AFAIK gilt übrigens auch
Code (perl): (dl )
\( %a , %b ) == (  \%a , \%b ) 

womit du noch mehr Backslashes einsparen könntest, wenn nur Referenzen übergeben werden.
TMTOWTDYOG (there's more than one way to dig your own grave)
Ronnie
 2008-02-13 22:48
#105856 #105856
User since
2003-08-14
2022 Artikel
BenutzerIn
[default_avatar]
@KurtZ: Sehr schöne Darstellung! Vielen Dank dafür.

@SchaubFD:
SchaubFD+2008-02-13 13:40:07--
Auch wenn meine Fragen nervig sind, sind Übergaben von Hashes nur über Referenzen möglich?

Perl unterscheidet zwischen Referenzen und Variablen in Form von Skalaren, Arrays (Listen) und Hashes (assoziative Listen). Wenn man sich an Referenzen gewöhnt hat ist das okay - auch wenn man explizit dereferenzieren muss. Ich verwende deshalb oft direkt Referenzen auf anonyme Arrays oder Hashes, sodass die Dereferenzierungssyntax für mich die "normale" Syntax ist. So richtig an das Konzept gewöhnt habe ich mich als ich viel das Modul CPAN:HTML::Template verwendet habe. Was gewöhnungsbedürftig gelöst ist (und sich mit Perl6 ändert) ist die Verwendung von Sigils ($@%) bei der Dereferenzierung. Generell kann man über Sinn und Unsinn von Sigils diskuttieren, aber in Perl-Code erlauben sie recht zügig eine Einschätzung mit was man es gerade zu tun hat, was gerade in einer dynamisch typisierten Sprache manchmal recht hilfreich sein kann. Andere Sprachen wie z.B. Python kennen keine Variablen im klassischen Sinne, sondern ausschließlich Referenz-Typen. Eine Zuweisung bindet ein (meist unveränderbares) Objekt auf der rechten Seite des = an ein Symbol auf der linken Seite. Die Derefernzierung erfolgt aber implizit. Problematisch an dieser Stelle ist, wenn man größere Datenstrukturen zusammensetzt und nicht bedenkt, dass eine Änderung der Inhalte über alle Symbole möglich ist (speziell bei Listen). Fairerweise muss man aber sagen, dass man sich in Perl auf dem selben Wege auch in den Fuss schiessen kann. Um auf deine Ausgangsfrage zurück zu kommen: Meistens ist es sinnvoll, wenn man Funktionen hat die mehrere Hashes etc. verarbeiten/zurückliefern, die eigentliche Problemstellung nochmal zu analysieren und evtl. auf Objekte umzusteigen. Objektorientierte Programmierung hilft einem Dinge die zusammengehören auch zusammenzubehalten, sowohl Daten als auch Funktionen (die dann aber Methoden genannt werden).

Gruß,
Ronnie
KurtZ
 2008-02-14 02:00
#105858 #105858
User since
2007-12-13
411 Artikel
BenutzerIn
[default_avatar]
@Ronnie: Vielen Dank fürs Lob :-)
(Aber ich zweifle ob Perls Objektorientierung für Anfänger eine Vereinfachung darstellen, insbesondere weil man den Pfeilsyntax nicht los wird.)

die Aufgabenstellung lässt sich locker vereinfachen, sehr selten braucht man mehrere Hashes zurückzugeben, oft reicht eine mehrfach verschachtelte Struktur, also
statt
Code (perl): (dl )
1
2
3
4
$A{1} = "Hugo";
$B{1} = "Schmitt";
$C{1} = "Vorsitzender";
my $Count = 1;

braucht man eher
Code (perl): (dl )
1
2
3
4
5
6
7
sub init_personen{
  my @personen;
  $personen[0]{ vorname } = "Hugo";
  $personen[0]{ nachname } = "Schmitt";
  $personen[0]{ stellung } = "Vorsitzender";
  return @personen;
}

Und schon braucht man nur noch ein Array zurückzugeben!
Will man also eine Kopie des Arrays, schreibt man dann
Code (perl): (dl )
@copy_personen=init_personen();


Will man aber ein Alias, dann sollte man einfach schon beim Aufruf eine Referenz aufs Zielarray übergeben, also:

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
sub init_personen{
  my $p_ref=shift; 
  $p_ref->[0]{ vorname } = "Hugo";
  $p_ref->[0]{ nachname } = "Schmitt";
  $p_ref->[0]{ stellung } = "Vorsitzender";
}

my @alias;
init_personen ( \@alias );
print $alias[0]{ nachname }


Natürlich könnte man auch mehrere Refs übergeben, wenn man wirklich mehrere Hashes braucht... im Grunde verlagert man so nur den Pfeilsyntax ins Innere der Routine, wo's weniger stört.

Noch zwo Optionen hätte man, die ich aber nicht wählen würde...

1. Wenn man partout nicht mit Pfeilen dereferenzieren will, kann auch den $$ syntax benutzen ( allerdings nur wenn man sicher ist, später den Überblick zu behalten, was man da tut ). Also

Code (perl): (dl )
  $$p_ref[0]{ vorname } = "Hugo"; 


2. Entsprechend "geprototyped" kann man sich auch Backslashes sparen

Code (perl): (dl )
1
2
3
4
5
6
7
8
sub init_personen ( \@ ) {
  my $p_ref=shift; 
  $p_ref->[0]{ vorname } = "Hugo";
  $p_ref->[0]{ nachname } = "Schmitt";
  $p_ref->[0]{ stellung } = "Vorsitzender";
}

init_personen( my @alias ) ;# Aufruf mit unsichtbarer impliziter Referenzierung 


Allerdings für eine einmalig aufgerufene Routine ist sowas eher overkill, schließlich macht man sich dadurch den Routinenaufruf ein Stück missverständlicher!

Zu guter Letzt kann man die Routine auch zusätzlich eine Kopie des Arrays zurückgeben, d.h. man kann sich je nach Aufrufmethode aussuchen was man raushaben möchte.


Noch ein Wort zu Packagevariablen ... besagtes Python hat meines Wissen selbst keine lexikalische Variablen und ich kann auch keinen echten Nachteil darin sehen in Fällen, in denen sich Aliase wirklich lohnen mit Typeglobs zu arbeiten.

Denn Aliase lohnen sich vor allem bei sehr häufig aufgerufenen Variablen, d.h. dass sie auch irgendwie "Packetcharakter" haben. Und durch lokalisieren mit "local" wird auch der Geltungsbereich eindeutig beschnitten.
TMTOWTDYOG (there's more than one way to dig your own grave)
SchaubFD
 2008-02-14 08:43
#105861 #105861
User since
2008-02-13
14 Artikel
BenutzerIn
[default_avatar]
Danke allen Antwortenden! Vorallem KurtZ, deine Darstellung hilft mir sehr.

PS: Meine Beispiele sollten wirklich nur Beispiele sein. Ich brauche den Index um später über die Hashes verschiedene Sortierungen durchführen zu können. Mehrdimensionale Hashes oder Kombinationen mit Listen wollte ich als Perl Anfänger vermeiden.
Struppi
 2008-02-14 11:56
#105871 #105871
User since
2006-02-17
628 Artikel
BenutzerIn
[Homepage]
user image
SchaubFD+2008-02-14 07:43:50--
Ich brauche den Index um später über die Hashes verschiedene Sortierungen durchführen zu können. Mehrdimensionale Hashes oder Kombinationen mit Listen wollte ich als Perl Anfänger vermeiden.
Wobei gerade diese für Anfänger eine enorme Erleichterung darstellen. Wenn man Referenzen vermeiden möchte bedeutet das bei komplexen Datenstrukturen (die offensichtlich vorliegen), dass du viele Verrenkungen machen musst, damit die Daten in einer vermeintlich einfachen Form vorliegen. Wie du ja selber und an den langen Erläuterungen gemerkt hast.

Darüber hinaus sollte man sich auch immer mal auf CPAN umschauen ob es hilfreiche Module gibt. Da der Umgang damit unbedingt zum programmieren in Perl dazu gehört (wie auch in allen anderen Programmiersprachen, Bilbliotheken zum Standardrepertoire gehören [ausser vielleicht bei PHP, aber dafür hast du dort mit 20.000 Funktionen zu kämpfen])
renee
 2008-02-14 12:47
#105880 #105880
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Ich habe perlreftut mal übersetzt: perlreftut
OTRS-Erweiterungen (http://feature-addons.de/)
Frankfurt Perlmongers (http://frankfurt.pm/)
--

Unterlagen OTRS-Workshop 2012: http://otrs.perl-services.de/workshop.html
Perl-Entwicklung: http://perl-services.de/
KurtZ
 2008-02-14 12:48
#105881 #105881
User since
2007-12-13
411 Artikel
BenutzerIn
[default_avatar]
@SchaubFD: Ich stell es mir schwer vor Hashes zu sortieren, verwechselst du da was mit Arrays?
TMTOWTDYOG (there's more than one way to dig your own grave)
Taulmarill
 2008-02-14 12:55
#105882 #105882
User since
2004-02-19
1750 Artikel
BenutzerIn

user image
renee+2008-02-14 11:47:12--
Ich habe perlreftut mal übersetzt: perlreftut


perldsc und perllol sind auch übersetzt.
$_=unpack"B*",~pack"H*",$_ and y&1|0& |#&&print"$_\n"for@.=qw BFA2F7C39139F45F78
0A28104594444504400 0A2F107D54447DE7800 0A2110453444450500 73CF1045138445F4800 0
F3EF2044E3D17DE 8A08A0451412411 F3CF207DF41C79E 820A20451412414 83E93C4513D17D2B
SchaubFD
 2008-02-14 15:40
#105898 #105898
User since
2008-02-13
14 Artikel
BenutzerIn
[default_avatar]
KurtZ+2008-02-14 11:48:01--
Ich stell es mir schwer vor Hashes zu sortieren, verwechselst du da was mit Arrays?


Nein, ich nutze den Index und sortiere später über zusammengesetzte Zeilen.

Beispiel (Pipe als Feldtrennung): Index, Datum, Uhrzeit

1 | 10.10.2007 | 08:15
2 | 09.10.2007 | 10:30
3 | 08.10.2007 | 23:15

Daraus mache ich

200710100815 | 1
200710091030 | 2
200710082315 | 3

Die Sortierte Liste gibt über den Wert dann:

200710082315 | 3
200710091030 | 2
200710100815 | 1

Der Index ist dann für mich der Zugriff auf die jeweiligen Werte. Ich möchte mir eine Datenbank ersparen und die Sortierung kann aus mehreren Feldern in Kombination bestehen. Ich brauche natürlich einen eigenen Sortierindex!

Übrigens meine jetzige Lösung (Hash) sieht nun so aus (habe auf eine ListenHash Variante umgestellt (spart 50% Code - Original Beispiel):

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
sub lese_email
{
my @email;
my $satz;
my $datei="email.csv";
my $buf;

open(femail, "< $datei") or die "Kann Datei <$datei> nicht lesen!";
$satz=0;
$buf=<femail>; # Kopfzeile ignorieren - auslassen
while (<femail>) {
(
$email[$satz]{kostenstelle},
$email[$satz]{kostenstellenname},
$email[$satz]{kostentraeger},
$email[$satz]{kostentraegername},
$email[$satz]{konto},
$email[$satz]{kontoname},
$email[$satz]{email},
$email[$satz]{name}
)=split(';');
chomp($email[$satz]{name});
$satz++;
}
close femail;
return @email;
}


Dank eurer Hilfe finde ich das richtig schön!
<< |< 1 2 3 >| >> 23 Einträge, 3 Seiten



View all threads created 2008-02-13 09:24.