如何在Perl中的getter方法中返回两个值?

时间:2017-02-16 19:44:03

标签: perl

use strict;
use warnings;

package LineSegment;

sub new
{       
        my $class = shift; 
        my ($ax, $ay, $bx, $by) = @_;
        my $self = {"ax"=>$ax,
                    "ay"=>$ay,
                    "bx"=>$bx,
                    "by"=>$by,
                   }; 
        bless ($self, $class);
        return $self;
}
sub getA{
        #Issue on get A
        my $self = shift;
        return ($self->{ax}, $self->{ay});
}
sub getB{
        #Issue on get B
        my $self = @_;
        return ($self->{bx}, $self->{by});
}
sub setA{
        #Can print correct value. Is the return statement where it goes wrong?
        my($self, $ax, $ay) = @_;
        $self->{ax} = $ax if defined($ax);
        $self->{ay} = $ay if defined($ay);
        print "Setting ax: $self->{ax}\n";
        print "Setting ay: $self->{ay}\n";

        return ($self->{ax}, $self->{ay});
}
sub setB{
         #Can print correct value. Is the return statement where it goes wrong?
        my($self, $bx, $by) = @_;
        $self->{bx} = $bx if defined($bx);
        $self->{by} = $by if defined($by);
        return $self->{bx}, $self->{by};
}
1;

我正在尝试创建一个名为LineSegment的类。 ax和ay是一个 点等等是bx和by。我不能让getA或getB返回我的内容 想。它们只返回第二个值,这对于getA来说是很好的 并为getB。我希望它返回两个值(ax,ay)或(bx,by)。 我怎么做到这一点?在我的setA和setB方法中,值 将打印。但是,我可以在setA和setB中将它们返回错误吗? 或者我的问题在于我的getter方法?

这是我的主要内容:

print "Starting Line Segment\n";
use LineSegment;
$line = new LineSegment(10,20,30,40);
$line->setA(15,10);
$a = $line->getA();
print "Point A is: $a\n";
  

这是我的Point类:

use strict;
use warnings;

#class name 
package Point;

#constructor
sub new
{
        my $class = shift;
        my($x, $y) = @_;
        my $self = {"x"=>$x,
                    "y"=>$y,
                   };
        bless ($self, $class);
        return $self;
}

sub getX{
        my($self) = @_;
        return $self->{x};
}
sub setX{
        my ($self, $x) = @_;
        $self->{x} = $x if defined($x);
        return $self->{x};
}
sub setY{
        my ($self, $y) = @_;
        $self->{y} = $y if defined($y);
        return $self->{y};
}
sub random{
        my $self = shift;
        my $range = 50;
        $self->{x} = int(rand($range));
        $self->{y} = int(rand($range));

        return ($self->{x}, $self->{y});
}
1;
  

更新了主要内容:

use strict;
use warnings;

    use Point;
    use LineSegment;

    my $line = LineSegment->new(Point->new()->random, Point->new()->random);
    my $pointA = $line->getA;
    my $pointB = $line->getB;
    printf "Point = (%d,%d)\n", $pointA->getX, $pointA->getY;

3 个答案:

答案 0 :(得分:6)

正如 Tanktalus 所指出的那样,您将返回两个值的列表,并期望能够将其视为单个Point对象。标量上下文中的列表评估列表的 last 元素,因此您只得到Y坐标

我在下面写了一些功能代码。可能让您感到困惑的一件事是哈希切片语法@{$self}{qw/ _x _y /} = @_

相同
$self->{_x} = $_[0];
$self->{_y} = $_[1];

您应该记住每个Perl源文件顶部的use strictuse warnings。您还应该避免使用$a$b,因为Perl会在内部使用它们。不管怎样,更长,更具描述性的标识符更好

如果我更改了您的Point.pm,以便其构造函数采用参数(我还修复了您的random方法)

Point.pm

use strict;
use warnings 'all';

package Point;

sub new {
    my $class = shift;

    my $self = { };
    @{$self}{qw/ _x _y /} = @_ if @_;

    bless $self, $class;
}

sub getX{
    my $self = shift;

    return $self->{_x};
}

sub getY{
    my $self = shift;

    return $self->{_y};
}

sub setX {
    my $self = shift;

    $self->{_x} = $_[0] if @_;

    return $self->{_x};
}

sub setY {
    my $self = shift;

    $self->{_y} = $_[0] if @_;

    return $self->{_y};
}

use constant RANGE => 50;

sub random {
    my $self = shift;

    $self->{_x} = int rand RANGE;
    $self->{_y} = int rand RANGE;

    return $self;
}

1;

并像这样写LineSegment.pm

LineSegment.pm

use strict;
use warnings 'all';

package LineSegment;

sub new {
    my $class = shift;

    my $self = { };
    @{$self}{qw/ _pA _pB /} = @_ if @_;

    bless $self, $class;
}

sub getA {
    my $self = shift;

    return $self->{_pA};
}

sub getB {
    my $self = shift;

    return $self->{_pB};
}

sub setA {
    my $self = shift;

    $self->{_pA} = $_[0] if @_;

    return $self->{_pA};
}

sub setB {
    my $self = shift;

    $self->{_pB} = $_[0] if @_;

    return $self->{_pB};
}

1;

然后我可以编写一个程序来完成我认为你想要的东西

main.pl

use strict;
use warnings 'all';

use Point;
use LineSegment;

my $line = new LineSegment(
    Point->new(10, 20),
    Point->new(30, 40),
);

$line->setA( Point->new(15, 10) );

my $point = $line->getA;

printf "Point = (%d,%d)\n",
    $point->getX,
    $point->getY;

输出

Point = (15,10)

答案 1 :(得分:3)

my ($ax, $ay) = $line->getA();

getA()返回变量列表,需要将其接收到变量列表中。阵列也可以工作,但这可能更清楚。

但那不是你想要的。你想要做的是让一个线段由两个Point对象组成(你可能也需要创建它们),每个Point对象都存储它自己的x和y坐标。然后你可以将点作为对象返回,并查询它们的x和y坐标,例如:

my $a_point = $line->getA();
print "Point A is (", $a_point->getX(), ",", $a_point->getY(), ")";

(你也可以让Point类覆盖字符串化,但我怀疑这比你想要的还要多。)

抱歉第一次没有抓住这个,但不仅仅是单字母变量名称一般不好,$a$b在perl中特别糟糕,因为它们是为{保留的{1}}功能。所以我在这里重新命名了。

通过更新,您的Point类缺少getY方法。您的主要脚本变为:

sort

并且您的LineSegment.pm变为:

use strict;
use warnings;
use LineSegment;

print "Starting Line Segment\n";
my $line = new LineSegment(10,20,30,40);
$line->setA(15,10);
my $p = $line->getA();
print "Point A is: (", $p->getX(), ",", $p->getY(), ")\n";

这可能有点矫枉过正,但正确的答案是只传入LineSegment中的Point对象,并让调用者创建Point对象而不是在这里按摩它们。根据我的经验,这使整个事情变得更加清晰。

答案 2 :(得分:1)

您可以通过BorodinTanktalus获得完整的答案 显示如何编写此类,以及其他注释。他们还强调段类应该充分利用点类。

这是一个重点。我们将问题的某个方面封装在一个类中。然后我们希望将该类用于问题的其他方面,这在面向对象的方法中至关重要。它通常需要在设计和编码方面进行迭代,才能使这些类正确。

本文通过为段的长度添加方法来演示该过程,其中提示添加其他方法。我还在你的课程中添加了一些其他部分

  • Point类中添加了一些对length方法有帮助的实用方法,这些方法通常属于那里。这是典型的 - 我们希望新的功能,并意识到其他类应该提供它的一部分(或全部)。

  • 默认值将添加到构造函数中。调用new后,如果可能,应初始化对象并准备就绪。您的Point::random方法用于此目的。

  • 将setter和getter合并为一个方法,该方法在使用参数调用时设置数据

一些评论遵循代码。

<强> Point.pm

package Point;

use strict;
use warnings;

sub new {
    my $class = shift;
    my $self = { };
    bless $self, $class;           # now we can call methods on $self
    if (@_) {
        @{$self}{qw(_x _y)} = @_;  # initialize via parameters
    } else { 
        $self->random();           # initialize using random()
    }
    return $self;
}

sub x {
    my $self = shift;
    $self->{_x} = $_[0] if $_[0];  # set if parameter was passed
    return $self->{_x};
}

sub y {
    my $self = shift;
    $self->{_y} = $_[0] if $_[0];
    return $self->{_y};
}

sub coords {
    my $self = shift;
    @{$self}{qw(_x _y)} = @_ if @_;
    return $self->{_x}, $self->{_y};
}

sub distance {
    my ($self, $pt) = @_; 
    my ($x1, $y1) = $self->coords();
    my ($x2, $y2) = $pt->coords();
    return sqrt( ($x1 - $x2)**2 + ($y1 - $y2)**2 );
}

sub random {
    my $self = shift;
    my $range = $_[0] // 50;
    $self->{_x} = int rand $range;
    $self->{_y} = int rand $range;
    return $self;
}

1;

random方法采用可选范围,因此$pt->random()$pt->random(10)都设置$pt的随机坐标。它具有默认50,使用defined-or operator//进行设置。由于它返回对象本身,您可以链接方法,如

my $pt = Point->new(10, 20);
my @coords = $pt->random()->coords();
print "@coords\n";

或者,因为new本身也会返回对象,甚至

my @coords = Point->new()->random(10)->coords();

虽然我们现在没有获得该物品,但这并没有多大用处。

<强> LineSegment.pm

package LineSegment;

use strict;
use warnings;
use Point;

sub new {
    my $class = shift;
    my $self = { };
    bless $self, $class;
    if (@_) { @{$self}{qw(_pA _pB)} = @_ }
    else    { @{$self}{qw(_pA _pB)} = (Point->new, Point->new) }
    return $self;
}

sub pA {
    my $self = shift;
    $self->{_pA} = $_[0] if $_[0];
    return $self->{_pA};
}

sub pB {
    my $self = shift;
    $self->{_pB} = $_[0] if $_[0];
    return $self->{_pB};
}

sub pts {
    my $self = shift;
    @{$self}{qw(_pA _pB)} = @_ if @_;
    return @{$self}{qw(_pA _pB)};
}

sub len {
    my $self = shift;
    return $self->{_pA}->distance($self->{_pB});
}

1;

如果没有传递任何参数来初始化段对象,构造函数中的默认值会为每个点调用Point的默认构造函数。

len()方法不需要坐标,因为我们向distance()添加了Point方法。它是自然的,在点类中是必需的,这比LineSegment计算要好。当然,我们通常需要在课堂上进行计算。考虑mid_point(段的),intersection(两段之间)等。

<强> main.pl

use warnings 'all';
use strict;
use feature 'say';

use Point;
use LineSegment;

my $line = LineSegment->new(
    Point->new(10, 20),
    Point->new(30, 40),
);

my $pt_A = $line->pA( Point->new(15, 10) );
my $pt_B = $line->pB;

printf "Point A = (%d,%d)\n", $pt_A->coords();
printf "Point B = (%d,%d)\n", $pt_B->coords();    
printf "Length of the segment: %.3f\n", $line->len();

my @coords = $pt_A->random(10)->coords();
say "Random point, set on existing object: @coords";

my $segm = LineSegment->new();
my @ends = $segm->pts();

print "Segment created with defaults, ends: ";
printf "(%d,%d) ", $_->coords()  for @ends;
say '';

打印

Point A = (15,10)
Point B = (30,40)
Length of the segment: 33.541
Random point, set on existing object: 3 8
Segment created with defaults, ends: (34,19) (16,14)

这里特别缺少的是各种检查。但是,一旦变得重要,应该开始关注Moose或相似但更轻的Moo

对问题中使用的new LineSegment()语法的评论。

Perl中的构造函数只是一个方法,但bless将对象放入类(包)中。名称new确实很常见,但这仅仅是一种惯例。因此&#34;正常&#34;调用构造函数的方法与任何其他方法ClassName->new()类似。

一个可以使用new ClassName,这被称为&#34; 间接对象表示法&#34; (或语法)。然而,这是perlobj本身有关它的说法(原始强调)

  

在文件句柄的情况下,不鼓励使用此语法,因为它可能会混淆Perl解释器。有关详细信息,请参见下文。

例如,另请参阅this post及其链接。只需使用ClassName->new()