Schrift
[thread]11630[/thread]

Allg. Frage zu Referenzen

Leser: 3


<< |< 1 2 >| >> 19 Einträge, 2 Seiten
toppsino
 2008-04-12 17:15
#108267 #108267
User since
2006-07-29
18 Artikel
BenutzerIn
[default_avatar]
Hallo!
Ich habe bisher Perl (nicht PERL, geändert 12.04.2008 22:07) nur für kleinere Aufgaben und Skripte verwendet, wollte jetzt aber mal etwas tiefer einsteigen. In dem Tutorial das ich gerade mache bin ich jetzt bei REFERENZEN (references) angekommen. Ich denke, dass ich das WIE - zumindest in Teilen - verstanden habe! Aber ich muss leider sagen, dass mir das WARUM immer noch nicht klar ist :( !!
Kann mir eine/einer von euch vielleicht mal in "einfachen" Worten oder Anhand eines kleinen Beispiels erklären WANN und WARUM man Referenzen einsetzt.
Sorry aber irgendwie blicke ich das noch nicht.
DANKE
GwenDragon
 2008-04-12 17:25
#108269 #108269
User since
2005-01-17
14555 Artikel
Admin1
[Homepage]
user image
Stell dir vor, du hast eine Funktion, an die du einen Hash, ein Array und mehrere Zeichenketten übergeben willst.

Beispiel wie es meist falsch gemacht wird:
Code: (dl )
1
2
3
4
5
6
my $UNDMEHR = 'Und mehr!';
my $NOCHWAS = 'Noch was? Nee!?';
my @ARRAY = (1, 2, 3, 4);
my %HASH = (Erste => 1, Zweite = 2, Dritte => 3, Letzte =>999);

meine_funktion(@ARRAY, %HASH, $NOCHWAS, $UNDMEHR);

Ohne Referenzen geht das jedenfalls beim Array und dem Hash nicht.
Also besser so:
Code: (dl )
1
2
3
4
5
6
my $UNDMEHR = 'Und mehr!';
my $NOCHWAS = 'Noch was? Nee!?';
my @ARRAY = (1, 2, 3, 4);
my %HASH = (Erste => 1, Zweite = 2, Dritte => 3, Letzte =>999);

meine_funktion(\@ARRAY, \%HASH, $NOCHWAS, $UNDMEHR);


In der Funktion kannst du so zugreifen:
Code: (dl )
1
2
3
4
5
6
7
8
9
sub meine_funktion { # \@ARRAY, \%HASH, $NOCHWAS, $UNDMEHR
my $array_ref = shift;
my $hash_ref = shift;
my $string1 = shift;
my $string2 = shift;

print 'Hashwert Zweite ist dann ', $hash_ref->{Zweite};
print 'Arraywert 3 ist dann ', $array_ref->[3-1];
}


Sicherlich lässt es sich viel besser noch schreiben, aber das soll ja ein Anfängerbeispiel sein.
die Drachin, Gwendolyn


Unterschiedliche Perl-Versionen auf Windows (fast wie perlbrew) • Meine Perl-Artikel

pq
 2008-04-12 18:44
#108273 #108273
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
ein weiterer grund, der mit dem von gwendragon genannten zusammenhängt:
manchmal will man hashes oder arrays an funktionen übergeben und sie in
der funktion verändern.
desweiteren spart es speicher und arbeit, wenn ein grosses array nicht für
jeden funktionsaufruf umkopiert werden muss.
und letztendlich ist objektorientierung in perl ohne referenzen nicht möglich.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
betterworld
 2008-04-12 19:07
#108277 #108277
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
pq+2008-04-12 16:44:27--
manchmal will man hashes oder arrays an funktionen übergeben und sie in
der funktion verändern.

Oder auch Skalare. Wenn es nur ein Skalar ist, macht man es meist so, dass man den veraenderten Skalar einfach zurueckgibt. Aber bei mehreren Skalaren (oder wenn der Rueckgabewert fuer etwas anderes gebraucht wird) bietet es sich an, Referenzen darauf an die Funktion zu uebergeben.
sid burn
 2008-04-12 19:45
#108278 #108278
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Quote
Hallo!
Ich habe bisher PERL nur für kleinere Aufgaben und Skripte verwendet, wollte jetzt aber mal etwas tiefer einsteigen.

Gut, erste Lektion. Es heißt "Perl" nicht "PERL". ;)

GwenDragon zeigt ein gutes Beispiel, aber irgendwie fehlt die Erklärung...


Daher hier nochmal eine kleine Erklärung von mir. Stell dir vor du möchtest eine Funktion schreiben die zwei Arrays übergeben bekommt und testet ob beide Arrays identisch sind. Hier in den Beispiel zeige ich erstmal nur was übergeben wird.

Du könntest letztendlich sowas versuchen.
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
my @a1 = qw( a b c );
my @a2 = qw( a b c );

is_array_equal(@a1, @a2);

sub is_array_equal {
my (@a1, @a2) = @_;

print "a: @_\n";
print "a1: @a1\n";
print "a2: @a2\n";
}



Das ganze klappt aber aus mehreren Gründen nicht mehr. Als erstes kommt der Funktionaufruf.

Code: (dl )
is_array_equal(@a1, @a2)


Wie du hoffenltich bisher weißt werden hier nicht zwei Argumente übergeben also zwei Arrays. Sondern die Arrays werden "flattened" und in einer liste umgewandelt und dann so der Subroutine übergeben.

Das siehst du dann auch in der Ausgabe der Subroutine. Alle Argumente sind in "@_" enthalten.

Code: (dl )
my ( @a1, @a2 ) = @_

Dieses würde natürlich auch nicht funktionieren. Da das erste @1 immer alles aus @_ enthalten würde und nichts mehr übrig lassen würde.

Um es also kurz zu sagen. Innerhlab der Subroutine hast du keine Information mehr welche Elemente zu Array1 gehörten oder welche Elemente zu Array2 gehörten.


Damit das ganze dann doch Funktioniert musst du nicht das Array an sich der Funktion übergeben, sondern du musst sozusagen eine Skalare Variable übergeben die auf dein Array zeigt. Und genau das ist es was eine Referenz macht. Um den oberen Code nochmals abzuändern.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
my @a1 = qw( a b c );
my @a2 = qw( a b c );

is_array_equal(\@a1, \@a2);

sub is_array_equal {
my ($a1, $a2) = @_;

print "a: @_\n";
print "a1: $a1\n";
print "a2: $a2\n";
}


Du übergibst nun deiner Funktion zwei Skalare Werte und jeder Skalar zeigt wiederrum auf einem Array. Dadurch bleibt nun die Information enthalten. Du kannst die Referenzen dereferenzieren um auf die Inhalte des Arrays zuzugreifen. Du solltest bisher dann wohl schon Wissen wie das geht daher erspare ih mich hier eine Erklärung.


Ein nächster Punkt ist das du bei Referenzen sozusagen nur die "Speicherstelle" zu dem Array übergibst. Veränderst du direkt Inhalte in deiner Funktion wird das Original Array bearbeitet. Dieser Punkt ist bei der Objekt Orientierung sehr wichtig wozu du hoffentlich später auch noch kommen wirst. Um ein Beispiel zu zeigen:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
my @a1 = qw( a b c );

change_array( \@a1 );

print "@a1\n";

sub change_array {
my ($a1) = @_;

$a1->[0] = "f";
}


Die Ausgabe des Programmes ist nun:
Code: (dl )
f b c


Da $a1 innerhalb der Subroutine auf das äußere Array zeigt. Und du das äußere Array direkt veränderst.


Und bevor jemand ankommt und mich versucht zu berichtigen. Ja es würde auch ohne Referenzen gehen.
Code: (dl )
1
2
3
4
5
6
7
8
9
my @a1 = qw( a b c );

change_array( @a1 );

print "@a1\n";

sub change_array {
$_[0] = "f";
}


Dieses würde ebenfalls korrekt "f b c" ausgeben. Da Perl by Default seine Elemente by Reference übergibt. Allerdiengs solltest du lieber das erste Beispiel nehmen. Da du dann direkt auf @_ zugreifen musst und bei mehreren Argumenten es ziemlich unleserlich wird. Und es ist flexibler. Zwei Arrays kann ich mit dieser Methode ja immer noch nicht übergeben.

Code: (dl )
my ( $a1 ) = @_;


Wenn dir dies noch etwas unklar ist. Meist steht am anfang ja meist soetwas wie oben. Das legt immer Kopien von den einzelnen Elementen in @_ an. Wenn eine Referenz in @_ ist dann wird zwar in diesem Fall auch eine Kopie angelegt. Aber Referenzen zeigen ja sozusagen immer nur auf irgendetwas anderes. Wenn du also eine Referenz kopierst. Zeigt auch die Kopie auf das gleiche. Durch das Derefernzieren gelangst du dann darauf worauf die Referenz zeigt.


Weitere Verwendung ist z.B. bei Tiefen Datenstrukturen. Sagen wir mal du möchtest mehrere Personen anlegen. Die Information zu einer Person legst du in einem Hash ab. Für jede Person musst du jetzt ein Hash anlegen und sagen wir mal du möchtest nun zu jeder Person den Namen ausgeben.

Du könntest also soetwas Coden:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
my %ich = (
name => 'Raab',
vorname => 'David',
geburtstag => '19.02.1983',
wohnort => 'Essen',
);

my %du = (
name => 'toppsino',
vorname => 'Dirk',
geburtstag => '1.1.1970',
wohnort => 'Bad Nauheim',
);

print "$ich{name}\n";
print "$du{name}\n";


Das hat aber etliche Nachteile. Du musst nun für jede weitere Person die du anlegen möchtest ein neuen hash mit Namen ausdenken. Und wenn du die Namen alle ausgeben möchtest dann musst du für jeden Hash eine print anweisung schreiben. bzw extra bei der Ausgabe hinzufügen.

Weiterhin ist das ganze nicht mehr zur Laufzeit veränderbar. Stell dir vor du möchtest ein Programm schreiben das nun zur Laufzeit Nach den Personendaten fragt. Ohne das Wissen von Referenzen müsstest du nun irgendwie ein hash erzeugen und diesen irgendwie abspeichern und dein Programmcode so abändern das er den neuen Namen ausgiebt.


Mit dem Wissen von Referenzen ist das ganze aber deutlich einfacher. Die Personendaten speicherst du immer noch in einem Hash. Anstatt allerdiengs die Hashes direkt namen zu geben Speicherst du den Hash in einem Array ab. Das Array ist dynamisch erweiterbar und du kannst auch zur Laufzeit einfach neue Werte dem Array anhängen. Sowas nennt man dann Tiefe Datenstruktur da du nun ein Array hast was mehrere Hashes enthält. Ein Array of Hashes oder kur AoH.

Der erste schritt sieht also so aus:

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
my %ich = (
name => 'Raab',
vorname => 'David',
geburtstag => '19.02.1983',
wohnort => 'Essen',
);

my %du = (
name => 'toppsino',
vorname => 'Dirk',
geburtstag => '1.1.1970',
wohnort => 'Bad Nauheim',
);

my @personen = ( \%ich, \%du );

for my $person ( @personen ) {
print $person->{name}, "\n";
}


Das was du bisher daraus gewonnen hast ist die flexibilität bei der Ausgabe. Du musst nun nicht mehr für jede Person einzelnd ein Print Statement schreiben. Sondern du gehst einfach das Arrays durch. Jeder Wert des Arrays zeigt auf einem Hash. Mit dem Derefernzieren (Pfeil) kannst du nun auf den Ursprünglichen hash zu greifen. Und dort jeweils den Key "name" ausgeben.

Ein weiterer Weg zur Flexibilität ist es nun anstatt vorher Hashes zu erzeugen und diese dann dem Array hinzuzufügen direkt Hashreferenzen zu erzeugen die im Array abgelegt werden. Um eine Arrayreferenz zu erzeugen werden geschweifte Klammern genutzt. Das solltest du eventuell schon Wissen.

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
my @personen = (
{
name => 'Raab',
vorname => 'David',
geburtstag => '19.02.1983',
wohnort => 'Essen',
},
{
name => 'toppsino',
vorname => 'Dirk',
geburtstag => '1.1.1970',
wohnort => 'Bad Nauheim',
},
);

for my $person ( @personen ) {
print $person->{name}, "\n";
}


Hier hast du jetzt nur noch ein Array. Die geschweiften Klammern innerhlab des Arrays erzeugen nun eine Hashreferenz. Auch eine sogenannte Anonyme Hashreferenz da es keinen Hashnamen mehr gibt um direkt auf den Hash zuzugreifen. Du kommst nur noch an die Elemente heran wenn du durch das Array durchgehst. In diesem Fall benötigen wir aber auch keinen Namen. Ehrlich gesagt waren sie nur im weg.



Der nächste Schritt ist die Gültigkeit von Datenstrukturen zu Wissen. Eine Datenstruktur, also Array, Hash etc. lebt solange solange noch eine Referenz auf diese Datenstruktur zeigt. Solange also irgendeine Referenz auf den Speicherbereich der Variable zeigt, wird diese Variable nicht zerstört. Sowas ist der sogenannte Garbage Collector ein sogenannter "Reference Counter".


Für das obere Beispiel zeige ich dann nämlich eine weitere Verwendung. Nämlich das erzeugen eines Hashes und das zurückgeben einer Referenz auf dem Hash in der Subroutine.

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
my @personen = (
{
name => 'Raab',
vorname => 'David',
geburtstag => '19.02.1983',
wohnort => 'Essen',
},
{
name => 'toppsino',
vorname => 'Dirk',
geburtstag => '1.1.1970',
wohnort => 'Bad Nauheim',
},
);

# Nach einer Person Fragen. Subroutine generiert
# aus den eingaben einen hash und gibt eine hashreferenz
# darauf zurück.
my $person = ask_person();

# Diese hashreferenz zu @personen hinzufügen
push @personen, $person;

# alle Namen ausgeben
for my $person ( @personen ) {
print $person->{name}, "\n";
}

# Subroutine die nach Personendaten fragt.
sub ask_person {
print "Bitte Name eingeben: ";
my $name = <STDIN>;

print "Bitte Vorname eingeben: ";
my $vorname = <STDIN>;

print "Bitte Geburtstag eingeben: ";
my $geburtstag = <STDIN>;

print "Bitte Wohnort eingeben: ";
my $wohnort = <STDIN>;

chomp( $name, $vorname, $geburtstag, $wohnort );

my %person = (
vorname => $vorname,
name => $name,
geburtstag => $geburtstag,
wohnort => $wohnort,
);

return \%person;
}
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
sid burn
 2008-04-12 19:45
#108279 #108279
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
Blöde 10K Zeichen Begrenzung.....




Hier ist also eine Subroutine die einen Hash intern von deinen benutzereingaben erzeugt. Eine Referenz darauf zurück gibt und diesen in einem Array Speichert. Da %person immer noch referenziert wird, wird der Hash nach beenden Subroutine nicht zerstört.

Was wichtig zu verstehen ist. Nach beenden der Subroutine verliert aber %person innerhlab der Subroutine seine gültigkeit. Aber es gibt ja ausserhalb der Subroutine ja immer noch eine Referenz die auf den Hash zeigt. Somit existiert dieser noch weiter.

Bei einem erneuerten Aufruf der Funktion musst du aber verstehen das dann wieder ein komplett neuer Hash erzeugt wird.

Ein weiterer Vorteil den ich noch nicht genannt habe. Wenn du eine Referenz einer Subroutine übergibst dann wird der eigentliche Inhalt nicht Kopiert. Das ist performanter. Stell dir vor du hast wirklich eien sehr große Datenstruktur wo 500MB Daten gespeichert sind (nur rein theoretisch). Wenn du es mittels Kopie übergeben würdest. Müssten nochmals 500MB Speicher addressiert werden. Also insgesamt 1GB Speicher.

Wenn du eine Referenz übergibst dann wird nur eine Speicheradresse übergeben wo sich diese 500MB befinden. Aber nicht die ganzen 500MB nochmals Kopiert.

Schneller, Speicherschonender.


Ich hoffe diese kleine Beispiele haben dir so ungefähr den Sinn dahinter gezeigt. Ansonsten würde ich dir raten einfach wieter zu Programmieren. Irgendwann macht es "Plopp!". ;)
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
betterworld
 2008-04-12 21:58
#108284 #108284
User since
2003-08-21
2613 Artikel
ModeratorIn

user image
Wow, wie lang hast Du daran geschrieben? :)
Ich stimme Dir uebrigens zu, es heisst nicht PERL.
sid burn
 2008-04-12 22:57
#108287 #108287
User since
2006-03-29
1520 Artikel
BenutzerIn

user image
betterworld+2008-04-12 19:58:10--
Wow, wie lang hast Du daran geschrieben? :)
Ich stimme Dir uebrigens zu, es heisst nicht PERL.

hmm, als ich angefangen habe gab es nur GwenDragons Posts. ^^
Nicht mehr aktiv. Bei Kontakt: ICQ: 404181669 E-Mail: perl@david-raab.de
toppsino
 2008-04-13 00:00
#108290 #108290
User since
2006-07-29
18 Artikel
BenutzerIn
[default_avatar]
Danke, das bringt etwas mehr Licht ins Dunkel!
Insbesondere das Beispiel von sid burn -
Quote
Weitere Verwendung ist z.B. bei Tiefen Datenstrukturen.

- hat das etwas klarer gemacht! (Aber müsste es nicht "tiefen" heissen ;)
Aber ich will nicht kleinlich sein und werde zukünftig nur noch Perl schreiben :)

Danke für die Mühe die Ihr euch gemacht habt.
renee
 2008-04-13 10:41
#108297 #108297
User since
2003-08-04
14371 Artikel
ModeratorIn
[Homepage] [default_avatar]
Was hier auch noch nicht verlinkt wurde ist 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/
<< |< 1 2 >| >> 19 Einträge, 2 Seiten



View all threads created 2008-04-12 17:15.