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;
答案 0 :(得分:6)
正如 Tanktalus 所指出的那样,您将返回两个值的列表,并期望能够将其视为单个Point
对象。标量上下文中的列表评估列表的 last 元素,因此您只得到Y坐标
我在下面写了一些功能代码。可能让您感到困惑的一件事是哈希切片语法@{$self}{qw/ _x _y /} = @_
与
$self->{_x} = $_[0];
$self->{_y} = $_[1];
您应该记住每个Perl源文件顶部的use strict
和use warnings
。您还应该避免使用$a
和$b
,因为Perl会在内部使用它们。不管怎样,更长,更具描述性的标识符更好
如果我更改了您的Point.pm
,以便其构造函数采用参数(我还修复了您的random
方法)
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
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;
然后我可以编写一个程序来完成我认为你想要的东西
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)
您可以通过Borodin和Tanktalus获得完整的答案 显示如何编写此类,以及其他注释。他们还强调段类应该充分利用点类。
这是一个重点。我们将问题的某个方面封装在一个类中。然后我们希望将该类用于问题的其他方面,这在面向对象的方法中至关重要。它通常需要在设计和编码方面进行迭代,才能使这些类正确。
本文通过为段的长度添加方法来演示该过程,其中提示添加其他方法。我还在你的课程中添加了一些其他部分
在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()
。