Thread Perl und @* (35 answers)
Opened by barney at 2023-11-09 09:47

Linuxer
 2023-11-10 15:11
#195466 #195466
User since
2006-01-27
3890 Artikel
HausmeisterIn

user image
Ich versuche es nochmal. Das erkannte "Problem" besteht aus zwei Komponenten, die hier zwangsläufig kombiniert sind:

  • 1.) Variablen mit Interpunktionszeichen, die nicht deklariert werden müssen, sondern eine Sonderbehandlung erfahren, siehe Perldoc:perlvar
    Das gilt für alle Typen; Skalare, Arrays, und Hashes
    Hier konkret: @* und auch %*

    Ausser den vordefinierten Variablen dieser Klasse (wie $., $?, $/, ...) sollte man die nicht in normalen Skripten nutzen. Klare Variablennamen sind toll und zu bevorzugen.

    Code (perl): (dl )
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    use strict;
    use warnings;
    use 5.024;
    
    # keine Fehler/Warnungen trotz strict+warnings
    say for @*;
    say for %*;
    
    # aber hier gibt's Fehler; haetten mit my, our, oder 'use vars' vorher deklariert werden muessen
    # aber ohne strict will man ja auch Aerger haben :-(
    say for @named_array;
    say for %hash_with_name;

    (Zum Testen die letzten beiden Zeilen erstmal weglassen oder kommentieren; dann einfügen oder aktivieren und nochmal testen.)


  • 2.) die Syntax der Postfix-Derefenzierung: siehe Perldoc:perlref Postfix-Dereference-Syntax

    Wenn man mit der neu eingeführten Syntax alle Elemente eines referenzierten Arrays aus einer einfachen oder komplexen Datenstruktur holen will, kann man das jetzt auch mit einem nachgelagerten ->@* tun.
    Ist soweit ja ganz ok.

    Code (perl): (dl )
    1
    2
    3
    4
    
    @elements = @$array_ref;
    @elements = @{ $array_ref };
    #oder eben
    @elements = $array_ref->@*;


    Gerade bei komplexen Strukturen mag das angenehmer sein, dass man keine geschweiften Klammern drumrum setzen muss.
    Man kann die Struktur von links nach rechts lesen und am Ende steht da, was man daraus holen will:

    Code (perl): (dl )
    1
    2
    3
    4
    5
    6
    7
    8
    
    # die geschweiften Klammern fuers Dereferenzieren koennen laestig erscheinen.
    # doof, wenn man am Ende feststellt, dass da ja ein Hash-Ref vorliegt; 
    # dann muss man in der Zeile zurueckgehen und das @{ zu einem %{ machen ...
    @elements = @{$hashref->[0]->{keyname}->[23]->{kinder}};
    
    # angenehmer: wenn mir am Ende bei ->{kinder} wieder einfällt, dass da ein Hash drinsteckt; 
    # kann das @* vor Ort zu einem %* gemacht werden... (Diskussion moeglich; hier nicht relevant)
    @elements = $hashref->[0]->{keyname}->[23]->{kinder}->@*;


Das Problem ist nun die Syntax am Ende: ->@*. So ist es ein Postfix-Deref, der alle Elemente der enthaltenen Referenz holen soll.

Wenn man das > vergisst, wird da eine Subtraktion -@* draus.
Oder wenn man das - weglässt, ein Vergleich >@*.

In beiden Fällen wird @* "plötzlich" als Array gewertet, ohne dass eine Warnung oder ein Fehler erscheint.
Eben weil diese Interpunktionsvariablen eine Sonderbehandlung erfahren (siehe oben oder in Perldoc:perlvar).

In beiden Fällen steht @* nun im skalaren Kontext und liefert die Anzahl seiner Elemente zurück; in der Regel dürfte das 0 sein.
Im ersten Fall wird also 0 von der Speicheradresse der Referenz abgezogen;
im zweiten Fall wird ein Vergleich durchgeführt, ob die Speicheradresse der Referenz größer 0 ist; normalerweise "ja", dargestellt durch die "1".


Je nach Situation ist dieser Fehler kaum zu erkennen.

Und das ist nicht auf die Array-Syntax beschränkt!

Ähnliches kann bei ->%* und auch passieren; wobei es da wahrscheinlich eher auffällt, weil:

Bei aktivierten Warnungen kommt eine Meldung, wenn man eine ungerade Anzahl von Elementen an einnen Hash zuweist.

Code (perl): (dl )
1
2
3
4
5
6
use warnings;  ## WICHTIG!!!!

my $hash_ref = { key => 'value' };

my %ergebnis = $hash_ref-%*;  ## odd number of elements
my %ergebnis = $hash_ref>%*;  ## odd number of elements


Ohne warnings kriegt man das aber auch nicht mit!


Bei $* hat man mehr Schutz, weil diese Variable früher mal vordefiniert war und jetzt diese Funktion nicht mehr hat.
Da sollte der Perl-Compiler selbst ohne strict und warnings schon beim Start aussteigen.


Veranschauungsskript für Array und Hash:
testskript.pl (16.6kb):

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#! /usr/bin/env perl
use strict;
use warnings;
use 5.024;

use Data::Dumper;
$Data::Dumper::Useqq = 1;

my $aRef = [ qw( 123 ) ];

say "\$aRef ist Array-Referenz mit Anzahl Elementen: ", 0+@$aRef;
say Data::Dumper->new([ $aRef ], [ 'aRef'] )->Useqq(1)->Dump;

# 
my @ergebnisse;

@ergebnisse = @$aRef;
say join " ", "Ergebnis (klassisch mit \@\$aRef):", @ergebnisse;

@ergebnisse = $aRef->@*;
say join " ", "Ergebnis (postfix deref mit ->\@\*):", @ergebnisse;

@ergebnisse = $aRef-@*;
say join " ", "Ergebnis (Tippfehler mit -\@\*):", @ergebnisse;

@ergebnisse = $aRef>@*;
say join " ", "Ergebnis (Tippfehler mit >\@\*):", @ergebnisse;

################
#

say "\n", "###"x20, "\n";


my $hRef = { foo => "bar" };

say "\$hRef ist Hash-Referenz mit Anzahl Paaren: ", 0+(keys %$hRef);
say Data::Dumper->new([ $hRef ], [ 'hRef'] )->Useqq(1)->Dump;

my %ergebnisse;

%ergebnisse = $hRef->%*;
say join " ", "Ergebnis (postfix deref mit ->\%\*):", %ergebnisse;
say Data::Dumper->new([ \%ergebnisse ], [ '*ergebnisse'] )->Useqq(1)->Dump;

my %ergebnisse = $hRef-@*;
say join " ", "Ergebnis (Tippfehler mit -\%\*):", %ergebnisse;
say Data::Dumper->new([ \%ergebnisse ], [ '*ergebnisse'] )->Useqq(1)->Dump;

@ergebnisse = $hRef>%*;
say join " ", "Ergebnis (Tippfehler mit >\%\*):", %ergebnisse;
say Data::Dumper->new([ \%ergebnisse ], [ '*ergebnisse'] )->Useqq(1)->Dump;




say "\n\n## ABER: die letzten zwei nochmal OHNE warnings...\n";

{ 
        no warnings;

        my %ergebnisse = $hRef-@*;
        say join " ", "Ergebnis (Tippfehler mit -\%\*):", %ergebnisse;
        say Data::Dumper->new([ \%ergebnisse ], [ '*ergebnisse'] )->Useqq(1)->Dump;

        @ergebnisse = $hRef>%*;
        say join " ", "Ergebnis (Tippfehler mit >\%\*):", %ergebnisse;
        say Data::Dumper->new([ \%ergebnisse ], [ '*ergebnisse'] )->Useqq(1)->Dump;

}


__END__

meine Beiträge: I.d.R. alle Angaben ohne Gewähr und auf Linux abgestimmt!
Die Sprache heisst Perl, nicht PERL. - Bitte Crossposts als solche kenntlich machen!

View full thread Perl und @*