Perl难以将值传递给函数

时间:2011-12-07 19:03:45

标签: perl

无法理解为什么函数login down的返回值与传递给它的内容不对应。

以下是我的代码片段

package This_package;
    .......   

    # returned from function that parses post data ($reqparam)
    my $thisuser = $$reqparam{"username"};

    # escape '@', username is an email
    $thisuser =~ s/@/\@/;
    my $thisuser_pass = $$reqparam{'password'};

    print $thisuser;      # ok
    print $thisuser_pass; # ok

    my $obj = new users;
    my $valid_user = $obj->login($thisuser, $thisuser_pass);
    .......

package Another_package;
    sub new {
        my ($class) = @_;
        my $self = {
            _login => undef,
            _create_user => undef,
            ....
            };
        bless $self, $class;
        return $self;
    }

    sub login ($$){
        my ($user, $pass) = @_;
        # some processing
        .....

       return $user;   # prints users=HASH(...)
       # return $pass; # prints the value of $user (the actual value)
                       # instead of the value of $pass
    }

尝试通过将一些代码从php转换为perl来学习perl。  我遇到了这个问题,我尝试了一些替代方案,但显然有一些我没有得到的东西!

3 个答案:

答案 0 :(得分:8)

当您调用类似

的功能时
 my $valid_user = $obj->login($thisuser, $thisuser_pass);

第一个参数通常以

完成
sub login
{
    my ( $self , $user , $password ) = @_;
}

您缺少 $ self

由于您缺少$ self,您用户实际上是对象,您的密码实际上是用户

如果您来自另一种受到反对的语言,例如 C ++,Java或C#,那么就是一个perl陷阱(没有双关语:))。另一个是,即使从一个对象方法,如果你想调用另一个成员方法,你必须使用像

这样的自我
$self->callAnotherObject( $user );

简单地打电话不要做

 callAnotherObject( $user );

Also I see that you are using function prototypes, It may not work as you intend it to be.

答案 1 :(得分:5)

当您使用面向对象的语法($obj->login($thisuser, $thisuser_pass))来调用子例程时,第一个参数将是对象本身。您应该说,您通常会看到面向对象的模块使用如下语法:

sub login {
    my ($self, $user, $pass) = @_;
    ...
}

顺便说一下,如果没有充分的理由,你不应该使用原型(($$))。 Perl中的原型的使用方式与其他语言中的原型不同,在任何情况下,当您使用间接语法调用子例程时,都会忽略原型(幸运的是,在您的情况下,因为您实际上是使用3个参数调用它)。

答案 2 :(得分:1)

你甚至看过流言终结者?

虽然你看到亚当和杰米做了非常非常危险的事情,但他们在每个节目的开头都警告你,“不要在家里这样做。”想想Perl prototypes以同样的方式。如果你使用它们,你很可能会被严重烧伤。


好的,现在谁在调用你的login功能?或者,也许更好,它是如何被称为的?

如果我使用你的Perl模块,我是否可以从我的主程序中调用 login 子程序?

my $package_obj = Another_package->new;
$package_obj->login($user, $password);

或者,这是为了方便你在包中使用的一些子程序,你将它用作一个简单的子程序,而不是像这样的私有方法:

package Another_package;

sub new {
   ...
}

sub foo {
  ...
  my $user = login ($user, $password);
}

如果你将login子程序作为一个简单的子程序 打包 ,就像在第二个例子中那样,一切都应该没问题。

但是,如果您将 login 子例程视为完整的承诺方法(就像我在第一个示例中所做的那样),您必须记住方法通过他们的类object作为子例程的第一个参数。

因此,你需要做这样的事情:

sub login {
   my $self     = shift;    #Pointer to the Another_package object I'm using
   my $user     = shift;
   my $password = shift;    #I just love lining things up!

   $self->{USER} = $user;   #Bad way of doing it.
   $self->{PASSWD} = $password;
   ...                      #Some processing.

   return $user;
}

为什么#Bad way of doing it发表评论?因为你真的想让你的内部尽可能分开。这样,如果您对Another_package类的结构进行了更改,则您的更改将在代码的特定部分中被隔离。它使调试更容易。

编写 login 子例程的更好方法是:

sub Login {                 #In standard Perl, methods are capitalized.
   my $self     = shift;    #Pointer to Another_package object
   my $user     = shift;    #Allow user to pass user and password in constructor
   my $password = shift;    #I just love lining things up!

   $self->User($user);      #Way better: This is a setter/getter method
   $self->Password($password);
   ...                      #Some processing.

   return $user;
}

在这个例子中,我使用setter / getter方法来设置我的用户名和密码。这样,我不必担心它们实际上是如何存储在我的对象中的。

这是使用setter / getter方法的Another_Package模块。我现在允许用户在他们调用 new 构造函数时传入用户和密码。

package Another_package;

    sub new {
        my $class = shift;
        my $user  = shift;
        my $password = shift;

        my $self = {};
        bless $self, $class;

        $self->User($user);
        $self->Password($password);
        ...
        return $self;
    }

    sub Login {
        my $self = shift;
        my $user = shift;
        my $pass = shift;

        $self->Password($pass);
        if (not defined $self->User($user)) {
           croak qq(Cannot log in without a user ID);
        }

        ...
        if ($login_successful) {
           return $self->User;    #Or maybe a session instant
        else {
           return;
        }
    }

请注意,在我的 new 构造函数子例程中,我创建了一个$self匿名哈希(my $self = {}),我马上祝福它。现在,$self已经是一个包对象,我可以调用一堆setter / getter方法来设置对象中的各个字段。我的 new 构造函数不知道我的实际 Another_module 对象是什么样的。

在我的 Login 方法子例程中,我也使用相同的setter / getter方法来设置用户和密码。同样,我的 Login 方法对这些字段如何存储在对象中一无所知。

您可能会注意到的另一件事是我在登录模块中设置了一个名为$login_successful的标量,以查看我的登录是否成功。在Perl中,如果方法失败,则通常不返回任何内容,或者在成功时返回一些内容。这样,用户的程序可以测试以查看调用是成功还是失败。例如,如果登录失败,用户可能希望在放弃之前尝试一些默认密码:

 my $package_obj = Another_package->new($user, $password);

 my $foo = $package_obj->Login;
 if (not defined $foo) {
     foreach my $password qw(swordfish s3x mon3y 7ucky) {
        $package_obj->Password($password);
        last if $foo = $package_obj->Login;
     }
     if (not defined $foo) {
        die "I don't know the password :-(";
     }
 }

那么,我的setter / getter方法是什么样的?它们实际上非常简单:

sub User {
    my $self = shift;
    my $user = shift;

    if(defined $user) {
         $self->{USER_INFO}->{USER} = $user;
    }
    return $self->{USER_INFO}->{USER};
}

sub Password {
    my $self = shift;
    my $pass = shift;
    if (defined $password) {
        $self->{USER_INFO}->{PASSWORD} = $pass;
    }
    return $self->{USER_INFO}->{PASSWORD};
}

为什么我将$user存储在$self->{USER_INFO}->{USER}而不是$self->{USER}?完全没有理由。但是,它确实显示Another_package模块的其余部分并不关心我在何处或如何存储用户和密码。