PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [Perl] defined undefined



Caveman
14-03-2006, 15:13
Hallo *,

Kann ich folgenden Perl-Code noch verkürzen?

1 if ( defined ${$self->{"db"}->readDB(DBrec, lastEMail)} ) # ist definiert?
2 {
3 ${$self->{"db"}->readDB(DBrec, lastEMail)} =~ /(\d+)/; # Zahl extrahieren
4 $eMailTime = $1;
5 } Das Problem dabei ist, dass ich zweimal über eine andere Klasse auf eine Datenbank zugreifen muss. Wenn ich allerdings die Zeile 3 in die if-Abfrage packe, dann bekomme ich einen Fehler, sobald die betroffene Stelle in der Datenbank nicht steht. Dies ist besonders deshalb unschön, da das ganze mit crontab läuft und deshalb eine Mail für die Fehlermeldung kommt.

michael.sprick
14-03-2006, 22:37
Hi,

gibt Deine readDB() Methode wirklich eine Referenz zurück? Du dereferenzierst ja zu einem Skalar.

Wenn ich jetzt Deinen Code richtig begreife, hast Du zwei Klassen. Klasse A hat ein Objekt der Klasse B als Attribut, richtig?

Dann sollte eigentlich sowas funktionieren:



#!/usr/bin/perl

use strict;
use warnings;

#########################
package DB;

sub new()
{
my $Invokation = shift(@_);
my $Class = ref($Invokation) || $Invokation;
my $this = {
someproperty => 'somevalue'
};
bless($this, $Class);
return($this);
}

sub readDB()
{
my $this = shift(@_);
return($this->{someproperty});
}

########################
package SomeClass;

sub new()
{
my $Invokation = shift(@_);
my $Class = ref($Invokation) || $Invokation;
my $this = {
db => DB->new()
};
bless($this, $Class);
return($this);
}

sub test()
{
my $this = shift(@_);
if($this->{'db'}->readDB() =~ m/(some.*)/i )
{
print $1;
}
}


######################
package main;

my $SomeObject = SomeClass->new();
$SomeObject->test();



Dein Codeschnipsel findest Du der SomeClass->test() wieder.

hth, michael

Caveman
14-03-2006, 23:51
Richtig erkannt, es handelt sich um die controller-Klasse (main), die auf die Datenbank-Klasse zugreift (hier die Zeit, zu der zuletzt eine E-Mail versandt wurde).
Der Wert, der abgefragt wird, ist eine Referenz auf ein Skalar (Dies kommt daher, dass die selbe Funktion auch ein Hash zurückgeben kann und dies als Referenz geschieht).

Das Problem ist leider nicht gelöst.
Denn, wenn ich die Abfrage nach dem Wert ohne define mache, schreibt crontab folgende Mail, wenn dieser nicht definiert ist:

Use of uninitialized value in pattern match (m//) at /home/dommel/temperatur/temperature.pl line 418. Dies soll verhindert werden.

Hier mal mein Kunstruktor und die angesprochene Funktion:


sub new # Konstruktor
{
my $this = shift;
my $class = ref($this) || $this;
my $path = shift;
my @names;
push @names, @_;

die "Keine Datenbank angegeben!" unless defined $names[0];

my $self = {
dbNames => \@names, # alle Datenbanken in Array
};

bless $self, $class; # Objekt erzeugen

foreach my $file (@{$self->{dbNames}}) #("temperature","sensorError","record")
{
tie(%{$self->{$file}}, 'SDBM_File', "$path$file", O_RDWR|O_CREAT, 0666);
}
return $self;
}


sub readDB
{
my $self = shift;
my $db = shift; # Datenoberbegriff
my $key = shift; # Datenschluessel
my $ret;

return $self->{$db} unless (defined $key);# gesamtes Hash wird zurueckgegeben

if (defined $self->{$db}->{$key})
{
$ret = $self->{$db}->{$key};
}
return \$ret; # ein Wert wird zurueckgegeben (ref)
}

und nochmal der geänderte Aufruf:


my $eMailTime = 0;
if ( ${$self->{"db"}->readDB(DBrec, lastEMail)} =~ m/(\d+)/ )
{
;
$eMailTime = $1;
}
Leider nicht in Farbe. Hat gerade irgendwie nicht funktioniert.

michael.sprick
15-03-2006, 09:59
hmm,

also wenn der Kommentar hinter dem folgenden Code bedeutet, was ich vermute:



foreach my $file (@{$self->{dbNames}}) #("temperature","sensorError","record")
{
tie(%{$self->{$file}}, 'SDBM_File', "$path$file", O_RDWR|O_CREAT, 0666);
}


Dann sieht das Objekt der klasse, in der die ReadDB Methode steht nach der foreach Schleife irgendwie so aus:



OBJECT-
|---'dbNames' = ArrayRef('Name1','Name2');
|---'temperature' = HashRef()
|---'senseError' = HashRef()
|---'record' = HashRef()


Im Aufruf benutzt Du dann aber


$self->{"db"}

Ist "db" hier nur ein Platzhalter für eine der drei Datenbanken? Wenn nein - das könnte ein Fehler sein.

Dann:




my $ret;
# $ret ist undef

return $self->{$db} unless (defined $key);
# nur wenn kein $key übergeben wurde

# $ret ist immernoch undef

# Nur wenn es die kombination $db->{$key} wirklich gibt!!!
if (defined $self->{$db}->{$key})
{
$ret = $self->{$db}->{$key};
}

# sonst ist $ret weiterhin undef
return \$ret;


Solltest Du also z.B. "db" tatsächlich benutzt haben, würde das genau die Warnung verursachen, die Du beschrieben hast. Das Gleiche gilt, wenn es $key nicht gibt.

Wenn Du nur die Warnung umgehen willst, und es für Dich OK bzw. gewollt ist, dass die Funktion ein undef zurückgibt, dann schreib einfach vor das Patternmatching

no warnings;

zwei andere Möglichkeiten sind
entweder:


my $ret = '';
# $ret erst sauber definieren

oder Du baust den Aufruf so um:


if($_ = ${$self->{"db"}->readDB(DBrec, lastEMail)} and m/(\d+)/i)
{
print $1;
}


hth, michael

Caveman
15-03-2006, 12:04
Danke, der letzte Code-Schnipsel war der, den ich suchte. Jetzt funktioniert es so wie ich will - ohne Mail mit Fehlermeldung.

Zur weiteren Erklärung:
$self{"db"} ist die Referenz auf das Objekt der Datenbank-Klasse.
Dieses enthält dann die drei Datenbanken "temperature", "sensorError" und "record". Wobei es hier um "record" geht. Dieser String steht in der Konstante DBrec. Auch die Konstante lastEMail beinhaltet einen String. Mit readDB(DBrec, lastEMail) wird also in der Datenbank "record" nach dem Eintrag der Konstante lastEMail gesucht und der Wert zurueck gegeben.

Zweck des ganzen ist es heraus zu finden, wann die letzte E-Mail versandt wurde und ob es an der Zeit ist eine weitere zu verschicken. Dies geschieht dann, wenn die Temperatur im Serverraum eine bestimmte Schwelle (über längere Zeit) überschreitet.