FHEM > Perl für FHEM-User

Nur zum Lernen mit der Bitte um konstruktive Kritik ...

(1/3) > >>

supernova1963:
Auf Empfehlung von @Beta-User verlagere ich das Thema hierhin (zeitlich von unten nach oben übernommen).

Offen bzw. von mir noch nicht wirklich verstanden sind die Anregungen von @Beta-User:

--- Zitat von: Beta-User am 29 August 2021, 08:13:38 ---- zum split könnte man auch noch ein paar Takte sagen, spare ich mir im Moment;
--- Ende Zitat ---

--- Zitat von: Beta-User am 29 August 2021, 08:13:38 ---- Welchen Vorteil bringt es while statt foreach zu verwenden?
--- Ende Zitat ---

--- Zitat von: Beta-User am 29 August 2021, 08:13:38 ---- Warum soll der json Abschluss nicht innerhalb der Schleife erfolgen?
--- Ende Zitat ---

Letzter Stand (da waehleRaum($) und waehleZone($) nahezu identisch sind hier nur noch waehleRaum($) aus 99_myXiaomiUtils.pm):

--- Code: ---##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


########################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennte Räume in
# einen publish Befehl übersetzen. Übergeben wird in der setList $EVENT.
sub
waehleRaum($)

{
# Parameter $EVENT: $COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_);

# Prüfung, ob mind. ein Raumname als Parameter übergeben wurde
if (@evtparts < 2) {
Log "3", "Raum: ".$evtparts[0]." in waehleRaum() nicht definiert!";
return undef;
}
my $i = 0;
# Das erste Element in $EVENT ist der Befehl und wird entfernt
shift(@evtparts);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';

###Räume definieren##########################################################
#
my %rid = (Wohnzimmer=>1, Esszimmer=>2, Kueche=>3);
#
###ENDE Definitionen##########################################################

# Überprüfen der übergebenen Raumnamen als Leerzeichen getrennte Parameter  
foreach my $room (@evtparts) {

# Prüfung, ob Raumname im hash rid vorhanden
if (!defined($rid{$room})) {
Log "3", "Raum: ".$evtparts[$i]." in waehleRaum() nicht definiert!";
next;
}

# json - ($set_json) muss bei dem ersten Raum kein Komma voran gestellt werden
if ($i == 0) {
$set_json = $set_json.$rid{$room};
}

# json - ($set_json) nach ltz. Raum mit Klammern abgeschliessen
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$rid{$room}.'"}]}';
}

# json - ($set_json) ansderenfalls wird ein Komma voran gestellt
else {
$set_json =  $set_json.','.$rid{$room};
}

$i = $i + 1;
}

# Übergabe des auszuführenden publish Befehls
Log("5", "waehleRaum() hat ".$set_topic." ".$set_json." übergeben!");
return $set_topic." ".$set_json;

}

# Ende waehle Raum / Räume
########################################################################

1;

--- Ende Code ---

Danke,

Gernot

Verlauf bis hierhin:


--- Zitat von: Beta-User am 29 August 2021, 08:13:38 ---@JensS: Bitte melden, wenn es zu OT ist (wie gesagt, ich fände das eigentlich in Perl für FHEM-User besser aufgehoben)...

Folgende Anmerkungen @supernova1963:
- "splice" ist eine nette Idee, hier aber eine "Spatzenkanone". Will man das erste Element aus einer Liste (ich finde "Liste" anschaulicher als "Array") löschen, ist "shift" m.E. das (schnellere und einfacherer) Mittel der Wahl - man muss das nicht zwingend dazu nutzen, einer Variable einen Wert zuzuweisen (den des gelöschten Elements); (vgl. auch "pop", und ergänzend noch push und unshift)
- zum split könnte man auch noch ein paar Takte sagen, spare ich mir im Moment;
- Es stellt sich die Frage, warum man überhaupt die Liste verändert, wenn man hinterher - im Prinzip - eine nummerische Schleife bauen will. Da kann man auch direkt "for (1..@evtparts-1) {" schreiben, dann man man die "Durchlaufnummer" in $_ stehen, und kann darüber ($evtparts[$_]) auf die Raumnamen in der Liste zugreifen.
- "zerstörend" auf die Liste zuzugreifen ist aber auch möglich und hier ggf. sogar eine gute Idee. Dann kann man nämlich die "Rest-Liste" für eine "while"-Schleife verwenden und das jeweils erste Element wieder innerhalb der Schleife per shift in eine Variable holen.

Sonstige Kritik:
- es gibt keine Prüfungen, ob
-- überhaupt ein Raum angegeben ist (return if @evtparts < 2;)
-- ein angegebener Raum gültig ist (next if !defined $rid{$room};)
Wegen letzterem Punkt würde ich den "finalen String" $set_json zunächst nicht belegen und erst nach der Schleife (siehe "qq") zusammenbauen, wenn der dann einen Wert hat.
(Schon klar, dass das kryptisch ist, viel Spaß beim knobeln ;) ).

--- Ende Zitat ---

--- Zitat von: supernova1963 am 28 August 2021, 18:58:38 ---Neuer Versuch 99_myXiaomiUtils.pm "besser" zu machen (immer noch rein theoretisch!):

--- Code: ---##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


########################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennten Räumen in
# einen publish Befehl übersetzen. Übergeben wird im Attribut des MQTT2_DEVICE setList { waehleRaum($EVENT) }.
sub
waehleRaum($)
{
#Parameter $EVENT: $EVTPART0='COMMAND' $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_);
# Das erste Element in $EVENT ist der Befehl und wird entfernt
@evtparts = splice(@evtparts, 0, 1);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';
my $i = 0;

# Räume definieren

my %rid = (Wohnzimmer=>1, Esszimmer=>2, Kueche=>3);

# ENDE Definitionen

# Erster und letzter Parameter werden separat zu $set_json hinzugefügt
foreach my $room (@evtparts) {
if ($i == 0) {
$set_json = $set_json.$rid{$room};
}
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$rid{$room}.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$room};
}
i = i + 1;
}

# Übergabe des auszuführenden publish Befehls
return $set_topic." ".$set_json;
}

# Ende waehle Raum / Räume
########################################################################

########################################################################
# Zonenname bzw. Namen von mehreren Leerzeichen getrennte Zonen in
# einen publish Befehl übersetzen. Übergeben wird im Attribut des MQTT2_DEVICE setList { waehleZone($EVENT) }.
sub
waehleZone($)
{
#Parameter $EVENT: COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_);
# Das erste Element in $EVENT ist der Befehl und wird entfernt
@evtparts = splice(@evtparts, 0, 1);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';
my $i = 0;
# Zonen definieren

my %zid = (Esstisch=>"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4");

# ENDE Definitionen

#   Überprüfen der übergebenen Zonennamen als Leerzeichen getrennte Parameter  
foreach my $zone (@evtparts) {
if ($i == 0) {
$set_json = $set_json.$zid{$zone};
}
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$zid{$zone}.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$zone};
}
i = i + 1;
}

# Übergabe des auszuführenden publish Befehls
return $set_topic." ".$set_json;

}

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

1;

--- Ende Code ---

@JensS: Ich bin nach-wie-vor gespannt auf die tatsächlich funktionierende Lösung

--- Ende Zitat ---

--- Zitat von: Beta-User am 28 August 2021, 10:54:45 ---Sieht auf den ersten Blick ok aus :) .
Im Prinzip dürfte das Mindestziel erreicht sein, aber über Details zum Coding "könnte" man dann durchaus nochmal etwas intensiver nachdenken (wäre eigentlich ein guter Kandidat für einen peer review im (User-) Perl-Bereich ;) ).
Kurzfassung, was mir an Ansatzpunkten im Kopf wäre (muß nicht immer klappen):  "split" geschickter  wählen (\s+), elsif/else durch direktes return vermeiden und das "innere" Zusammenbauen in eine (1..@arr)-Schleife verpacken...

--- Ende Zitat ---

--- Zitat von: supernova1963 am 27 August 2021, 18:24:02 ---Nur zum "Mitlesen" und lernen ...

Ich bin gespannt auf die korrekte Umsetzung bzw. Lösung ...

Mein nicht getesteter laienhafter Vorschlag für das setList Attribut:

--- Code: ---attr Sauger setList sauge_Raeume:noArg xiaomi {"action": {"siid": 4, "aiid": 13}}\
sauge_Zonen:noArg xiaomi {"action": {"siid": 6, "aiid": 5}}\
waehle_Zone:Esstisch { waehleZone($EVENT) }\
waehle_Raum:Wohnzimmer,Esszimmer,Küche { waehleRaum($EVENT) }\
waehle_Raeume { waehleRaum($EVENT) }\
waehle_Zonen { waehleZone($EVENT) }
--- Ende Code ---

Mein nicht getesteter laienhafter Vorschlag für 99_myXiaomiUtils.pm:

--- Code: ---##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


########################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennte Räume in
# einen publish Befehl übersetzen. Übergeben wird in der setList $EVENT.
sub
waehleRaum($)

{
my ($COMD,$ROOM1,$ROOM2,$ROOM3,$ROOM4,$ROOM5) = split(/ /,@_);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';

# Räume definieren

my %rid = (Wohnzimmer=>1, Esszimmer=>2, Kueche=>3);

# ENDE Definitionen

        #   Überprüfen der übergebenen Raumnamen als Leerzeichen getrennte Parameter
if ($ROOM1 eq "" || !defined($ROOM1) ) {
return undef;
}
else {
$set_json = $set_json.$rid{$ROOM1},
}

if ($ROOM2 eq "" || !defined($ROOM2) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$ROOM2};
}
if ($ROOM3 eq "" || !defined($ROOM3) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$ROOM3};
}
if ($ROOM4 eq "" || !defined($ROOM4) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$ROOM4};
}
if ($ROOM5 eq "" || !defined($ROOM5) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$rid{$ROOM5}.'"}]}';
}

# Übergabe des auszuführenden publish Befehls
return $set_topic." ".$set_json;

}

# Ende waehle Raum / Räume
########################################################################

########################################################################
# Zonenname bzw. Namen von mehreren Leerzeichen getrennte Zonen in
# einen publish Befehl übersetzen. Übergeben wird in der setList $EVENT.
sub
waehleZone($)
{
my ($COMD,$ZONE1,$ZONE2,$ZONE3,$ZONE4,$ZONE5) = split(/ /,@_);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';

# Zonen definieren

my %zid = (Esstisch=>"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4");

# ENDE Definitionen

# Überprüfen der übergebenen Zonennamen als Leerzeichen getrennte Parameter
if ($ZONE1 eq "" || !defined($ZONE1) ) {
return undef;
}
else {
$set_json = $set_json.$zid{$ZONE1},
}

if ($ZONE2 eq "" || !defined($ZONE2) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$ZONE2};
}
if ($ZONE3 eq "" || !defined($ZONE3) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$ZONE3};
}
if ($ZONE4 eq "" || !defined($ZONE4) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$ZONE4};
}
if ($ZONE5 eq "" || !defined($ZONE5) ) {
$set_json = $set_json.'"}]}';
}
else {
$set_json =  $set_json.','.$zid{$ZONE5}.'"}]}';
}

return $set_topic." ".$set_json;

}
# Ende waehle Zone / Zonen
########################################################################

1;

--- Ende Code ---

--- Ende Zitat ---

--- Zitat von: JensS am 26 August 2021, 19:34:46 ---Hallo,

mein Saugroboter empfängt seine Anweisungen als JSON per MQTT.

--- Code: ---waehle_Zone_Esstisch:noArg xiaomi {"set_properties": [{"siid": 6, "piid": 2, "value":"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4"}]}
sauge_gewaehlte_Zone:noArg xiaomi {"action": {"siid": 6, "aiid": 5}}
sauge_alles:noArg xiaomi {"action": {"siid": 2, "aiid": 1}}
waehle_Raueme:noArg xiaomi {"set_properties": [{"siid": 6, "piid": 2, "value":"1,2"}]}
sauge_Raeume::noArg xiaomi {"action": {"siid": 4, "aiid": 13}}
...
--- Ende Code ---
Nun würde ich gern vereinfachte Anweisungen "set Sauger sauge (Esstisch|alles|Raeume)" bzw. "set Sauger waehle (Zone|Raeume) (Esstisch|Wohnzimmer und Kueche)" nutzen.
Ist das im MQTT2DEVICE machbar oder muss ich einen anderen Weg suchen?

Gruß Jens

--- Ende Zitat ---

Beta-User:
Vorab mal Danke, dass du die Anregung aufgegriffen hast. In deinem "Pseudocode" sind ein paar Dinge drin, die es m.E. wert sind, mal aufgegriffen zu werden, v.a., was Schleifen über Arrays angeht.

"split"
a) Das ist zum einen eine Perl-"built-in Funktion. Bei solchen (und nur bei solchen) kann/darf man die Klammern auch weglassen (im Unterschied zu "Pseudo-built-ins", die durch die Verwendung von Prototypes entstehen. Zu Prototypes will ich hier aber nichts weiter sagen, das hier nur am Rande und auch im Code nicht verändert; weitere Beispiele sind im Code unten entsprechend kommentiert).
b) Intern ist das erste Argument eine regex. Und da darf man zum einen dem Compiler gleich sagen, dass man einen match sucht, und zum anderen gibt es die (hier nicht relevante) Option, da "hinten" auch weitere Optionen mit zu verwenden. Weiter kann man es dem Compiler einfacher machen, wenn man klarer sagt, wo Anfang und Ende der regex sind.
Hier mal zwei Varianten:

--- Code: ---my @evtparts = split m/\s+/, @_;
--- Ende Code ---

--- Code: ---my @evtparts = split m{\s+}, @_;
--- Ende Code ---

"while statt for"
a) foreach kann man direkt aus dem Perl-Wortschatz streichen, es ist funktional identisch mit "for"...
(Ergänzend: Wer weiß, was unless bedeutet, kann das auch direkt wieder vergessen...)
b) Du hattest die Idee, die Liste mit den Argumenten "zerstörend" zu modifizieren (durch das splice). Das "zerstörende" Rausnehmen von Elementen aus der Liste kann man auch nutzen, um die durchzugehen, ohne vorher groß die Länge zu ermitteln. Das macht den Code kompakter, da man weniger Variablen braucht. (Siehe Code unten). Wenn man mal verstanden hat, wie das funktioniert, ist es m.E. am Ende einfacher zu lesen...

"Warum den Zusammenbau außerhalb der Schleife"
Wenn man den Code insgesamt etwas anders aufbaut, kann man das Schleifenergebnis gleich dazu nutzen, um durch eine einzige Abfrage festzustellen, ob es überhaupt (sinnvolle) Angaben durch den User gab. Dazu wirft man erst mal eine nur initialisierte Variable (ohne Wert) in den Raum und schaut hinterher, ob die geändert wurde (also einen Wert hat). Da hier "zwischendurch" nicht viel passiert, wenn es gar keine Angabe gab, kann man das ohne großen Effizienzverlust auch "hinterher" machen.

Beispiel (auch von mir nicht getestet; es wird hoffentlich klarer, wenn man die auskommentierten Teile rauswirft):

--- Code: ---sub
waehleRaum($)

{
#    Parameter $EVENT: $COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
    my @evtparts = split m{\s+/}, @_;
#    Das erste Element in $EVENT ist der Befehl und wird entfernt
    shift @evtparts; #Beta-User: ebenfalls Perl built-in

    my $set_topic = "xiaomi";
    my $set_json; #Beta-User: nur initialisieren = '{"set_properties": [{"siid": 6, "piid": 2, "value":"';

###Räume definieren##########################################################
#   
    my %rid = (Wohnzimmer=>1, Esszimmer=>2, Kueche=>3);
#   
###ENDE Definitionen##########################################################
       
#    Überprüfen der übergebenen Raumnamen als Leerzeichen getrennte Parameter     
    while (@evtparts) {
        my $room = shift @evtparts;
#        Prüfung, ob Raumname im hash rid vorhanden
        if ( !defined $rid{$room} ) { #Beta-User: Perl-built-in
            Log("3", "Raum: $room in waehleRaum() nicht definiert!"); #Beta-User: KEIN Perl-built-in...!; einfache Quotes => Variable wird extrapoliert
            next;
        }
       
#        json - ($set_json) muss bei dem ersten Raum kein Komma voran gestellt werden
                $set_json .= $set_json ? ",$rid{$room}" : $rid{$room}; #Beta-User: kürzere Schreibweise, je nachdem, ob schon was da steht, wird ein Komma vorangestellt oder nicht
        #if ($i == 0) {
            #$set_json = $set_json.$rid{$room};
        #}

#        json - ($set_json) nach ltz. Raum mit Klammern abgeschliessen
        #elsif ($i == @evtparts -1) {
        #    $set_json =  $set_json.','.$rid{$room}.'"}]}';
        #}

#        json - ($set_json) ansderenfalls wird ein Komma voran gestellt
        #else {
        #    $set_json =  $set_json.','.$rid{$room};
        #}
       
        #$i = $i + 1;
    }

#    Übergabe des auszuführenden publish Befehls
        return if !$set_json; # hat die Variable nach der Schleife keinen Inhalt, gab es keine (gültige) Raumangabe...
        $set_json = qq{{"set_properties": [{"siid": 6, "piid": 2, "value":"
        $set_json"}]}}'; # Zusammenbau am Ende
    Log("5", "waehleRaum() hat ".$set_topic." ".$set_json." übergeben!");
    return "$set_topic $set_json"; #Beta-User: einfacher zu lesen...

}
--- Ende Code ---

Hoffe, das ist soweit einleuchtend?

(Da wir es hier mit MQTT2_DEVICE zu tun haben, könnte man ggf. sogar den Topic und/oder den Hash mit den Raumzuweisungen durch entsprechende Attribut-Angaben machen, aber das ist dann ggf. was für den anderen Thread und ich müßte erst mal in den Modul-Code schauen, welche Angaben in setList ersetzt werden ($DEVICETOPIC, $NAME, evtl. $JSONMAP).

supernova1963:
Einfach nur genial, @Beta-User …

Jetzt habe auch ich es begriffen, welche Vorteile der Zusammenbau außerhalb der Schleife hat.
Auch die Hinweise zu split kann ich nachvollziehen, und, foreach und unless sind vergessen.

An die "perlspezifische Kurzschreibweise" von "if … then" kann ich mich jedoch ebenso wenig gewöhnen, wie an die Variablenauswertung in double quoted strings. Aber du hast natürlich recht, ich hätte korrekterweise auch single quotes verwenden müssen.

Vielen Dank für die lehrreichende Erklärungen,

Gernot

Beta-User:
Danke für die Rückmeldung :) , freut mich, dass es dir weitergeholfen hat.

Bzgl. Quotes und "müssen": Viele Wege führen nach Rom, und es war ja nicht "falsch", die Concatenation "dreckig" zu schreiben.

Die "Kurzschreibweise" ist afaik gar nicht Perl-spezifisch - falls du nicht "elsif" meinst, sondern

--- Code: ---$foo = $bla eq "string" ? "this" : "that";
--- Ende Code ---
Diesen "ternären Operator" habe ich auch teils in meinen MySensors-Sketchen drin, C (bzw. C++) müßte das also auch kennen, siehe auch die Hinweise in https://www2.icp.uni-stuttgart.de/~hilfer/lehre/100-online/skriptum/html_book00/node34.html.

Wenn man sich mal dran gewöhnt hat, finde ich das - sauber notiert (!) - eigentlich in den meisten Fällen gut lesbar (auch in Attributen, da ist irgendwann eine if-else-Cascade mit "Vollcode" komplett unübersichtlich...)


Da wir grade bei C waren: Anlass war ja auch das mit den Schleifen gewesen. Vorhin habe ich hier im Forum das hier gefunden (Auszug, da sind irgendwelche Variablen vorher schon vorhanden, aber ich hoffe, es ist trotzdem erkennbar, dass es einfach nur zwei verschachtelte Schleifen sind):

--- Code: ---  my $res = 0;
  for ($j = 0; $j < $len; $j++) {
    $val = $data[$j];
    for ($i = 0; $i < 8; $i++) {
      $tmp = ($res ^ $val) & 0x80;
      $res <<= 1;
      $res &= 0xFF;
      if ($tmp != 0) {
        $res ^= 0x31;
      }
      $val <<= 1;
    }
  }
  return $res;
--- Ende Code ---

Perlcritic bezeichnet sowas als "unschöne C-Style-loop" - wieder kein Verbrechen, aber eben "unschön" (level 2 (?), also "eigentlich vernachlässigbar).

Die "Perl-spezifische Schreibweise" wäre (ungetestet, und ich bin nicht ganz sicher, ob man $len oder $len-1 als höchsten Wert notieren muss (ich meine nein, weil die Erhöhung nach dem Schleifendurchlauf stattfindet); geht ja vor allem um's Prinzip):

--- Code: ---  my $res = 0;
  for my $j (0..$len) { #Beta-User: Dieser Parameter $j ist nur in diesem lexical scope gültig und überschreibt auch einen außerhalb dieser Schleife existenten $j nicht!
    $val = $data[$j];
    for (0..7) {
      $tmp = ($res ^ $val) & 0x80;
      $res <<= 1;
      $res &= 0xFF;
      if ($tmp != 0) {
        $res ^= 0x31;
      }
      $val <<= 1;
    }
  }
  return $res;

--- Ende Code ---

Auch hier ist der Code im Ergebnis etwas kompakter und benötigt weniger Variablen, und man muss sich nicht um "$j" kümmern, was das "Umfeld" angeht, sondern kann "einfach so draufloscoden". $i ist hier ganz raus, man könnte (wie immer in Perl-Schleifen) dann auch $_ verwenden, wenn man den Wert doch braucht.
Nach meiner Erfahrung muss man aber grade beim Verschachteln von Schleifen (wie in obigem Beispiel) dann aufpassen, dass man sich nicht selbst durcheinanderbringt - ich würde daher im Sinne der Lesbarkeit dann in der inneren Schleife doch "for my $i ..." schreiben, wenn die grade aktuelle innere "Laufvariable" benötigt wird.

Ergo: unter Perlcritic-Gesichtspunkten zwar "ferner liefen", aber "schöner", wenn man es Perl-like macht :) .


Und als Ergänzung zu obigem "ternärem Operator" und der externen Fundstelle noch.

Da findet sich

--- Code: ---int i1   =       3 > 4 ? 0 : 1;    //  i1   is 1
--- Ende Code ---

In meinen Codings findet sich "//" auch sehr häufig, hat dann aber eine komplett andere Bedeutung als in "C" - es ist kein Kommentar, sondern (vielleicht aus Sicht echter Perl-Experten etwas "dreckig" formuliert) eine sehr spezielle Form eines "ternären Operators": 

Perl "defined-or".

Um Abzusichern, dass unser Code-Beispiel wirklich nur ausgeführt wird, wenn auch ein Argument übergeben wird (in denem Code macht das die Prototype-Prüfung), würde ich z.B. schreiben:

--- Code: ---sub waehleRaum {
    my $arg = shift // return;
    my $var = shift // q{Wert};
    [...]
--- Ende Code ---
bedeutet in Textform: führe ein return aus, wenn versucht wird, den Code ohne Argument auszuführen (oder mit echtem undef als erstem Argument), und weise der Variable $var den Wert "Wert" zu, falls der Code aufgerufen wird, ohne dass ein zweiter (valider) Parameter übergeben wird. (das "q" ist hier nur zur Ergänzung des Quote-Themas von oben, vollständige Fassung siehe https://www.perlmonks.org/?node_id=401006).
Zu defined-or siehe z.B. https://www.perl.com/article/32/2013/7/6/Use-the-logical-or-and-defined-or-operators-to-provide-default-subroutine-variable-behaviour/ (hab's nur überflogen).

JensS:
Bin erst heute zum testen gekommen. Mit ein paar kleinen Änderungen läuft's.

--- Code: ---##############################################
# $Id: myXiaomiUtils.pm
#
# Save this file as 99_myUtils.pm, and create your own functions in the new
# file. They are then available in every Perl expression.

package main;

use strict;
use warnings;

sub
myXiaomiUtils_Initialize($$)
{
  my ($hash) = @_;
}

# Enter you functions below _this_ line.


########################################################################
# Raumnamen bzw. Name von mehreren Leerzeichen getrennte Räume in
# einen publish Befehl übersetzen. Übergeben wird in der setList $EVENT.
sub
waehleRaum($)
{
# Parameter $EVENT: $COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_[0]);

# Prüfung, ob mind. ein Raumname als Parameter übergeben wurde
if (@evtparts < 2) {
Log "3", "Raum: ".$evtparts[0]." in waehleRaum() nicht definiert!";
return undef;
}
my $i = 0;
# Das erste Element in $EVENT ist der Befehl und wird entfernt
shift(@evtparts);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":';

###Räume definieren##########################################################
#
my %rid = (Wohnzimmer=> 1, Esszimmer=> 2, Kueche=> 3);
#
###ENDE Definitionen##########################################################

# Überprüfen der übergebenen Raumnamen als Leerzeichen getrennte Parameter
foreach my $room (@evtparts) {

# Prüfung, ob Raumname im hash rid vorhanden
if (!defined($rid{$room})) {
Log "3", "Raum: ".$evtparts[$i]." in waehleRaum() nicht definiert!";
next;
}

# json - ($set_json) muss bei dem ersten Raum kein Komma voran gestellt werden
if ($i == 0) {
$set_json = $set_json.$rid{$room}.'}]}';
}

# json - ($set_json) nach ltz. Raum mit Klammern abgeschliessen
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$rid{$room}.'}]}';
}

# json - ($set_json) ansderenfalls wird ein Komma voran gestellt
else {
$set_json =  $set_json.','.$rid{$room}.'}]}';
}

$i = $i + 1;
}

# Übergabe des auszuführenden publish Befehls
Log("5", "waehleRaum() hat ".$set_topic." ".$set_json." übergeben!");
return $set_topic." ".$set_json;

}

# Ende waehle Raum / Räume
########################################################################

########################################################################
# Zonenname bzw. Namen von mehreren Leerzeichen getrennte Zonen in
# einen publish Befehl übersetzen. Übergeben wird im Attribut des MQTT2_DEVICE setList { waehleZone($EVENT) }.
sub
waehleZone($)
{
# Parameter $EVENT: $COMMAND $EVTPART1 $EVTPART2 $EVTPART3 $EVTPART4 $EVTPART5 ...
my @evtparts = split(/\s+/,@_[0]);

# Prüfung, ob mind. ein Raumname als Parameter übergeben wurde
if (@evtparts < 2) {
Log "3", "Zone: ".$evtparts[0]." in waehleZone) nicht definiert!";
return undef;
}
my $i = 0;
# Das erste Element in $EVENT ist der Befehl und wird entfernt
shift(@evtparts);
my $set_topic = "xiaomi";
my $set_json = '{"set_properties": [{"siid": 6, "piid": 2, "value":';

###Räume definieren##########################################################
#
my %zid = (Esstisch=>'"-0.25,-2.4,-0.25,-0.1,2.2,-0.1,2.2,-2.4"', Eingang=>'"-0.58,-10,-0.58,-7.4,2.3,-7.4,2.3,-10"');
#
###ENDE Definitionen##########################################################

# Überprüfen der übergebenen Zonennamen als Leerzeichen getrennte Parameter
foreach my $zone (@evtparts) {

# Prüfung, ob Zonenname im hash rid vorhanden
if (!defined($zid{$zone})) {
Log "3", "Zone: ".$evtparts[$i]." in waehleZone() nicht definiert!";
next;
}

# json - ($set_json) muss bei der ersten Zone kein Komma voran gestellt werden
if ($i == 0) {
$set_json = $set_json.$zid{$zone}.'}]}';
}

# json - ($set_json) nach ltz. Zone mit Klammern abgeschliessen
elsif ($i == @evtparts -1) {
$set_json =  $set_json.','.$zid{$zone}.'}]}';
}

# json - ($set_json) ansderenfalls wird ein Komma voran gestellt
else {
$set_json =  $set_json.','.$zid{$zone}.'}]}';
}

$i = $i + 1;
}

# Übergabe des auszuführenden publish Befehls
Log("5", "waehleZone() hat ".$set_topic." ".$set_json." übergeben!");
return $set_topic." ".$set_json;

}

####################################
1;
--- Ende Code ---

Vielen Dank für eure Hilfe! Damit lässt sich der Viomi deutlich besser steuern.

Gruß Jens

Navigation

[0] Themen-Index

[#] Nächste Seite

Zur normalen Ansicht wechseln