[beantwortet] Frage zu hash und array

Begonnen von andies, 20 Februar 2024, 18:27:13

Vorheriges Thema - Nächstes Thema

andies

Ich habe eine Perl Frage und hoffe, ich bin in diesem Unterforum richtig aufgehoben (für Anfängerfragen scheint mir das nicht zu passen). Ich habe eine while-Schleife, in der Datensätze in einem String $l ausgelesen werden. Die Datensätze sehen etwa so aus:
$l = "2024-02-01 00:01:15 18.6"
$l = "2024-02-01 00:03:17 18.6"
...
$l = "2024-02-19 00:03:25 19.6"
$l = "#4:Gartentor.temperatur.*::"

Man erkennt ein Muster. Diese Daten sollen in ein array (genauer ein array of hashes: Daten und Werte getrennt sowie weitere Aangaben). Das mache ich bisher wie folgt:
my @series = ();
my %series_single = ();
my @data_array = ();
...
while (...){
     $l = some_function();
     if ($l =~ m/^[^#]/){
        my ($datum, $wert) = split(" ", $l);
        push @data_array, [$datum, $wert];
     } else {
        ... # einige weitere Werte fuer %series_single{key}=$value;
        $series_single{data} = [ @data_array ];
    push @series, \%series_single;
     }
}
Wenn ich das oben beschriebene Muster in den Daten habe, klappt alles. Problematisch wird es, wenn nicht eine, sondern mehrere "Reihen" in $l ausgelesen werden können (die while-Schleife liest alle Werte von $l auf einmal aus), also beispielsweise so ein Muster
$l = "2024-02-01 00:01:15 18.6"
$l = "2024-02-01 00:03:17 18.6"
...
$l = "2024-02-19 00:03:25 19.6"
$l = "#4:Gartentor.temperatur.*::"
$l = "2024-02-01 00:01:15 8.6"
$l = "2024-02-01 00:03:17 8.6"
...
$l = "2024-02-19 00:03:25 9.6"
$l = "#4:Haustuer.temperatur.*::"
Dann wird in das data_array nicht nur die erste ($datum, $wert)-Folge geschrieben, sondern die beiden series werden miteinander vermischt.

Ich habe dann ChatGPT4.0 gefragt, was ich da tun soll und man hat mir empfohlen, den data_array() zwischendurch zu löschen:
        ...
        $series_single{data} = [ @data_array ];
    push @series, \%series_single;
        @data_array = (); # <=== NEU
        ...
     } else {
Das klappt nicht, weil durch die dynamische Zuweisung beim löschen des @data_array auch in den bereits gespeicherten $series_single{data} die Daten entfernt werden.

Leider komme ich nicht richtig damit klar, dass in Perl arrays und hashes eindimensional sind und man faktisch ständig mit Zeigern arbeiten muss. In Pascal, das ich besser kenne, war das einfacher.

Hat jemand einen Tipp, wie ich hier weiterkomme? Ziel soll es sein, dass @series ein array ist, das mehrere hash %series_single{data} enthält, in dem alle [$datum,$wert] eines devices zu finden sind:
@series
     => { %series_single{data} }
          => [ [$datum, $wert] ]    
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann

Beta-User

Vermutlich habe ich die Frage noch nicht richtig verstanden:

Du hast eine Liste, ähnlich wie sie "show preprocessed input" im svg-wizzard anzeigt, aber die "Kennerzeile" für bestimmte Device/Reading-Kombinationen kommt immer mal wieder. Die Werte bis zu jeweils dieser Zeile sollen hinterher zu einem einzigen Array zusammengefasst werden?

Diese Liste steht _insgesamt_ und einmalig in "$l"?

Dann müßte man zunächst $l als Array begreifen, und dann zeilenweise durchgehen.

Dabei alles erst mal in ein (leer vordefiniertes) Puffer-Array pushen, bis die "was bin ich"-Zeile kommt, dann alles in den zugehörigen Hash pushen, das Puffer-Array wieder leeren und dann weitermachen...

Paßt das gedanklich?
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

andies

Genau das ist es, was ich suche. Und der Code kommt in der Tat aus dem SVG-Modul, das ich für eCharts umschreiben will. $l ist schon der zerlegte Riesenarray, der durch die while-Schleife zerlegt und eben pro Durchlauf schrittweise in $l geschrieben wird.

Vorhin kam mir der Gedanke, ob man das Pufferarray nicht als Array of Array definiert, so was wie @pufferarray{$index} und der $index springt dann mit jeder Kennerzeile eins weiter?
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann

andies

Also jetzt habe ich folgendes versucht
my @series = ();
my %series_single = ();
my @data_array = ();
my $series_index = 0;
...
while (...){
    $l = some_function();
    if ($l =~ m/^[^#]/){
        my ($datum, $wert) = split(" ", $l);
        push @{$data_array[$series_index]}, [$datum, $wert];# <== veraendert
    } else {
        ... # einige weitere Werte fuer %series_single{key}=$value;
        $series_single{data} = [ @{$data_array[$series_index]} ]; # <== veraendert
    push @series, \%series_single;
        $series_index++;
    }
}
Das führt aber nicht zum gewünschten Ergebnis. Statt dessen wird alles bis zur ersten Kennerzeile dreimal in den hash geschrieben - und die beiden anderen Datenpaare, mit anderen Kennerzeilen, ignoriert:
"series":[
{"type":"line","step":"start","smooth":"0.3",
"data":[["2024-02-01 00:01:15","18.6"]...["2024-02-20 21:05:37","31.9"],],"areaStyle":{},"name":"Rücklauf"},

{"type":"line","step":"start","smooth":"0.3",
"data":[["2024-02-01 00:01:15","18.6"]...["2024-02-20 21:05:37","31.9"]],"areaStyle":{},"name":"Rücklauf"},

{"type":"line","step":"start","smooth":"0.3",
"data":[["2024-02-01 00:01:15","18.6"]...["2024-02-20 21:05:37","31.9"]],"areaStyle":{},"name":"Rücklauf"}
],
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann

andies

PS Das habe ich vergessen, oben zu schreiben: was man nicht machen kann, ist, das Pufferarray zu füllen, es in den hash hinüberzuschieben und danach zu leeren. Perl erlaubt das nicht, sondern löscht beim leeren nicht nur das Pufferarray, sondern auch die Werte, die daraus im hash gespeichert wurden.

(Wahrscheinlich trage ich für Beta-User Eulen nach Athen. Ich bin halt Perl-Neuling.)
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann

andies

#5
Ich kann das Problem etwas genauer eingrenzen. Ich habe am Code folgendes geändert
          ...
          push @series, \%series_single;
          Log3 undef, 1, "series_index ".$series_index; # <=== NEU
          Log3 undef, 1, "dump series ".Dumper(\@series); # <=== NEU
            $series_index++;
          ...
und mir mal angeschaut, was bei $series_index=2 herauskommt. Das ist interessant:
2024.02.21 08:09:14 1: series_index 2
2024.02.21 08:09:15 1: dump series $VAR1 = [
          {
            'step' => 'start',
            'type' => 'line',
            'smooth' => '0.3',
            'areaStyle' => {},
            'name' => 'Rücklauf',
            'data' => [
                        [
                          '2024-02-01 00:01:15',
                          '18.6'
                        ],
                        ...
                        [
                          '2024-02-21 08:07:26',
                          '30.8'
                        ]
                      ]
          },
          $VAR1->[0],
          $VAR1->[0]
        ];
Das heißt also, dass die Pufferarrays  @{$data_array[$series_index]} gelöscht werden, sobald ich $series_index hochzähle. Jetzt müsste ich nur wissen warum.

Nachtrag: Eine weitere Analyse ergibt, dass das Problem genau an dieser Stelle liegt:
push @series, \%series_single;
Während \%series_single in allen Durchläufen die korrekten Werte enthält, werden beim Hochzählen von $single_index die bisherigen (früheren, alten) Werte von \%series_single gelöscht/überschrieben/als VAR=0 gesetzt.
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann

Beta-User

Zitat von: andies am 21 Februar 2024, 08:15:30Nachtrag: Eine weitere Analyse ergibt, dass das Problem genau an dieser Stelle liegt:
Du verwendest hier eine Referenz auf ein Array statt des Arrays selbst (oder den Inhalt der Referenz zu kopieren). Daher wird das auch dynamisch gelöscht, richtig wäre es, den vorhandenen Wert mit dem neuen Inhalt zu ergänzen...
Anders gesagt: Es sind zwei "Fehler" im Code: a) der Inhalt wird neu geschrieben ("="), und es wird b) die Referenz ("\") verwendet.
Und das Konzept
HASH{Devicename_Readingname}->ARRAY{Einzelwerte}
 scheint mir schon richtig zu sein, oder eben
HASH{Devicename}->HASH{Readingname}->ARRAY{Einzelwerte}Hängt halt von der weiteren Vararbeitung ab, die du vorhast.

my %series;
my %series_single;
my @data_array;
...
while (...){
    $l = some_function(); #get single line from preprocessing of svg generator
    if ($l =~ m/^[^#]/){
        my ($datum, $wert) = split(" ", $l);
        push @data_array, [$datum, $wert];
    } else {
        ... # einige weitere Werte fuer %series_single{key}=$value;
        my ($dev, $read) = ...; #per regex Device und Reading rausfischen
        push @{$series{$dev}->{$read}}, @data_array;
        @data_array = ();
    }
}
Problem dabei: Wenn man sich mit der Referenzierung irgendwo vertut, schmiert FHEM ab, also: keine Gewähr, dass die "Siegel" korrekt sind...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

andies

#7
Danke, jetzt habe ich es geschafft. Es liegt in der Tat daran, dass die "feste Variable" %series_single sowohl im selben Scope (Durchlauf) zugewiesen und gleichzeitig die Referenz darauf mehrfach verwendet wird. Das geht schief bei Perl.

Ich habe %series_single ersetzt durch @{$series[$series_index]} und damit läuft alles. Danke! 

Nachtrag. Hier der Code, sonst bleibt das so kryptisch
my @series = ();
my @data_array = ();
my $series_index = 0;
...
while (...){
    $l = some_function();
    if ($l =~ m/^[^#]/){
        my ($datum, $wert) = split(" ", $l);
        push @{$data_array[$series_index]}, [$datum, $wert];
    } else {
        ... # einige weitere Werte fuer @{$series[$series_index]}{key}=$value;
        @{$series[$series_index]}{data} = [ @{$data_array[$series_index]} ];
        $series_index++;
    }
}
und dann verarbeitet man @series gleich weiter.
FHEM 6.1 auf RaspPi3 (Raspbian:  6.1.21-v8+; Perl: v5.32.1)
SIGNALduino (433 MHz) und HM-UART (868 MHz), Sonoff, Blitzwolf, Somfy RTS, CAME-Gartentor, Volkszähler, Keyence-Sensor, Homematic-Sensoren und -thermostat, Ferraris-Zähler für Wasseruhr, Openlink-Nachbau Viessmann