Perl 什么';这是一个很好的方法;保险丝;不同指针指向的位置?
我有许多指向内存中不同(或相同)位置的指针。我想实现一种机制,允许我们“融合”给定指针子集指向的位置 我现在正在使用perl 5.6.1,但我对其他语言的实现持开放态度。我在perl中提出了以下愚蠢的实现:Perl 什么';这是一个很好的方法;保险丝;不同指针指向的位置?,perl,pointers,memory,reference,hdl,Perl,Pointers,Memory,Reference,Hdl,我有许多指向内存中不同(或相同)位置的指针。我想实现一种机制,允许我们“融合”给定指针子集指向的位置 我现在正在使用perl 5.6.1,但我对其他语言的实现持开放态度。我在perl中提出了以下愚蠢的实现: my $ref1 = \1; my $ref2 = \2; print "${$ref1} : ${$ref2}\n"; # <-- prints 1 : 2 fuse(\$ref1, \$ref2); # <-- Make $ref2 point to sa
my $ref1 = \1;
my $ref2 = \2;
print "${$ref1} : ${$ref2}\n"; # <-- prints 1 : 2
fuse(\$ref1, \$ref2); # <-- Make $ref2 point to same location as $ref1
print "${$ref1} : ${$ref2}\n"; # <-- prints 1 : 1 (which is correct)
sub fuse
{
${$_[1]} = ${$_[0]};
}
my$ref1=\1;
我的$ref2=\2;
打印“${$ref1}:${$ref2}\n”;# 我几乎放弃了,后来我偶然发现了一个神奇的东西,它似乎是为了解决这个问题而发明的。以下是我使用的代码:
use Scalar::Util qw( weaken );
my $ref1 = {}; $ref1->{voltage} = 1; weaken( $ref1->{parent} = $ref1 );
my $ref2 = {}; $ref2->{voltage} = 2; weaken( $ref2->{parent} = $ref2 );
my $ref3 = {}; $ref3->{voltage} = 3; weaken( $ref3->{parent} = $ref3 );
my $ref4 = {}; $ref4->{voltage} = 4; weaken( $ref4->{parent} = $ref4 );
print "@{[map(get_vol($_), ($ref1, $ref2, $ref3, $ref4))]}\n";
# Above line print 1 2 3 4
fuse($ref1, $ref2); # <-- Second argument gets set to first
print "@{[map(get_vol($_), ($ref1, $ref2, $ref3, $ref4))]}\n";
# Above line print 1 1 3 4
fuse($ref4, $ref3);
set_vol($ref3, 5);
print "@{[map(get_vol($_), ($ref1, $ref2, $ref3, $ref4))]}\n";
# Above line print 1 1 5 5
fuse($ref2, $ref3);
set_vol($ref3, 7);
print "@{[map(get_vol($_), ($ref1, $ref2, $ref3, $ref4))]}\n";
# Above line print 7 7 7 7
sub fuse
{
my ($node1, $node2) = ($_[0], $_[1]);
$node2 = $node2->{parent} while ($node2->{parent} != $node2);
$node2->{parent} = $node1;
}
sub get_vol
{
my $node = shift;
$node = $node->{parent} while ($node != $node->{parent});
return $node->{voltage};
}
sub set_vol
{
my $node = shift;
$node = $node->{parent} while ($node != $node->{parent});
$node->{voltage} = shift;
}
这个基本实现依赖于一个类属性,所有不相交的“融合”节点组都由它们的值设置关键帧。它们会根据需要在每次融合时进行更新和合并
use warnings;
use strict;
use feature 'say';
use FindBin qw($RealBin);
use lib $RealBin; # to load from ./
#use Data::Dump qw(dd);
use Nodes;
my $n1 = Nodes->new(volt => 10);
my $n2 = Nodes->new(volt => 20);
my $n3 = Nodes->new(volt => 30);
my $n4 = Nodes->new(volt => 40);
say "\nFuse n1 with (set to) n3:";
$n1->fuse_with($n3); # n1 is now at same voltage as n3
say "\tvoltage for node ", $_->label, " is: ", $_->volt
for ($n1, $n2, $n3, $n4);
say "\nFuse n4 with (set to) n2:";
$n4->fuse_with($n2); # n4 is now same as n2
say "\tvoltage for node ", $_->label, " is: ", $_->volt
for ($n1, $n2, $n3, $n4);
say "\nFuse n1 with (set to) n4:";
$n1->fuse_with($n4); # n1 is now same as n4, and so are n2 and n3
say "\tvoltage for node ", $_->label, " is: ", $_->volt
for ($n1, $n2, $n3, $n4);
# dd \%Nodes::Fused;
Nodes.pm
package Nodes;
use warnings;
use strict;
use feature 'say';
#use Data::Dump qw(dd);
our $Label = 0;
our %Fused; # disjoint groups ( value => { label => node, ... }, ... )
sub new {
my ($class, %args) = @_;
my $self = { _volt => $args{volt}, _label => ++$Label };
say "New node: volt = ", $self->{_volt}, ", label = ", $self->{_label};
$Fused{$self->{_volt}} = { $self->{_label} => $self };
return bless $self, $class;
}
sub volt {
my ($self, $val) = @_;
$self->{_volt} = $val if $val;
return $self->{_volt};
}
sub label { return $_[0]->{_label} }
sub fuse_with {
my ($self, $node) = @_;
# Retrieve groups that have $self or $node
my %groups = map {
( exists $Fused{$_}->{$self->{_label}} or
exists $Fused{$_}->{$node->label} )
? ($_ => $Fused{$_}) : ()
} keys %Fused;
# Add these nodes if they are in no groups, or
# Remove %groups from %Fused, fuse them into new one, update voltage
if (not keys %groups) {
$Fused{$node->volt}->{$_->label} = $_ for ($self, $node);
$self->{_volt} = $node->volt;
}
else {
delete $Fused{$_} for keys %groups;
$Fused{$node->volt} = { map { %{$groups{$_}} } keys %groups };
$Fused{$node->volt}->{$node->label} //= $node; #/
$Fused{$node->volt}->{$self->{_label}} //= $self; #/
$Fused{$node->volt}->{$_}->{_volt} = $node->volt
for keys %{$Fused{$node->volt}};
}
# dd \%Fused;
}
sub cleanup {
my ($self, $voltage) = @_;
if ($voltage) { # new voltage (and label) for the fused group
$Fused{$voltage} = $Fused{$self->{_volt}};
delete $Fused{$self->{_volt}};
$Fused{$voltage}->{$_}->{_volt} = $voltage
for keys %{$Fused{$voltage}};
}
$self->DESTROY;
}
# Must be called manually, via cleanup(), when object leaves scope
sub DESTROY {
my ($self) = @_;
return if ${^GLOBAL_PHASE} eq 'DESTRUCT';
delete $Fused{$_}->{$self->{_label}} for keys %Fused;
}
return 1;
这张照片
New node: volt = 10, label = 1
New node: volt = 20, label = 2
New node: volt = 30, label = 3
New node: volt = 40, label = 4
Fuse n1 with (set to) n3:
voltage for node 1 is: 30
voltage for node 2 is: 20
voltage for node 3 is: 30
voltage for node 4 is: 40
Fuse n4 with (set to) n2:
voltage for node 1 is: 30
voltage for node 2 is: 20
voltage for node 3 is: 30
voltage for node 4 is: 20
Fuse n1 with (set to) n4:
voltage for node 1 is: 20
voltage for node 2 is: 20
voltage for node 3 is: 20
voltage for node 4 is: 20
原因正是方便的class属性,它保留对对象的引用,因此不会自动调用析构函数
另一种方法是在每个对象中都有“融合”列表。如果有许多节点,并且融合通常是因为每个对象都必须重新生成整个列表O(N2),那么这将非常昂贵。这是对电路建模时可能出现的情况,因此我保留了class属性
再多说几句
- 这是它需要做的,但它缺少一些零碎的东西
- 它依赖于一个类属性,如果涉及到它,什么不是最干净的设计。它缠住物体,创造出一个全局实体,这在原则上是反对物体独立的
- 缺少一些基本方法,特别是“取消融合”节点和独立设置新值(并在需要时更新所有融合节点)
- 需要检查。需要一些低级的优化
我相信
- 您希望能够将一个熔断器组的任何部分与另一个熔断器组的任何部分熔断李>
- 您希望能够设置值,以便更新融合集的每个部分
这意味着以下程序定义了预期行为:
use strict;
use warnings qw( all );
use feature qw( say );
use FindBin qw( $RealBin );
use lib $RealBin;
use Wire qw( );
my $o1 = Wire->new( voltage => 1 );
my $o2 = Wire->new( voltage => 2 );
my $o3 = Wire->new( voltage => 3 );
my $o4 = Wire->new( voltage => 4 );
say join " ", map $_->get_voltage(), $o1, $o2, $o3, $o4; # 1 2 3 4
$o2->fuse($o1);
$o3->fuse($o4);
$o1->fuse($o3);
say join " ", map $_->get_voltage(), $o1, $o2, $o3, $o4; # 4 4 4 4
$o1->set_voltage(5);
say join " ", map $_->get_voltage(), $o1, $o2, $o3, $o4; # 5 5 5 5
$o3->set_voltage(6);
say join " ", map $_->get_voltage(), $o1, $o2, $o3, $o4; # 6 6 6 6
本课程实现了以下目标:
package Wire;
use strict;
use warnings qw( all );
sub new {
my ($class, %args) = @_;
my $voltage = $args{voltage} // 0;
my $self = bless({}, $class);
$self->{shared_voltage} = { value => $voltage, backrefs => [] };
push @{ $self->{shared_voltage}{backrefs} }, \( $self->{shared_voltage} );
return $self;
}
sub get_voltage { $_[0]{shared_voltage}{value} }
sub set_voltage { $_[0]{shared_voltage}{value} = $_[1]; }
sub fuse {
my ($self, $new) = @_;
my $old_sv = $self->{shared_voltage}; my $old_sv_br = $old_sv->{backrefs};
my $new_sv = $new->{shared_voltage}; my $new_sv_br = $new_sv->{backrefs};
for my $backref (@$old_sv_br) {
$$backref = $new_sv;
push @$new_sv_br, $backref;
}
}
sub DESTROY {
my ($self) = @_;
@{ $self->{shared_voltage}{backrefs} } =
grep { $_ != \( $self->{shared_voltage} ) }
@{ $self->{shared_voltage}{backrefs} };
}
1;
通过将对融合节点的引用列表与共享值一起存储,可以实现该结果。这与Perl中写时复制字符串使用的方法相同。熔合结构如下所示:
+-$o1--+ +-Wire----------------+
| Ref -------------->| +-shared_voltage--+ | +-anon hash------+
+------+ +---------->| Reference ------------------>| +-value------+ |
| | +-----------------+ | / / / | | 4 | |
| +---------------------+ | | | | +-backrefs---+ |
| | | | | | Reference -------+
| | | | | +------------+ | |
+-$o2--+ | +-Wire----------------+ | | | +----------------+ |
| Ref -----(-------->| +-shared_voltage--+ | | | | |
+------+ | +-------->| Reference -------+ | | +------------------------+
| | | +-----------------+ | | | |
| | +---------------------+ | | | +-anon array-----+
| | | | +-->| +-0----------+ |
| | | | | | Reference -------------+
+-$o3--+ | | +-Wire----------------+ | | | +-1----------+ | |
| Ref -----(-(------>| +-shared_voltage--+ | | | | | Reference -----------+ |
+------+ | | +------>| Reference ---------+ | | +-2----------+ | | |
| | | | +-----------------+ | | | | Reference ---------+ | |
| | | +---------------------+ | | +-3----------+ | | | |
| | | | | | Reference -------+ | | |
| | | | | +------------+ | | | | |
+-$o4--+ | | | +-Wire----------------+ | +----------------+ | | | |
| Ref -----(-(-(---->| +-shared_voltage--+ | | | | | |
+------+ | | | +---->| Reference -----------+ | | | |
| | | | | +-----------------+ | | | | |
| | | | +---------------------+ | | | |
| | | | | | | |
| | | | | | | |
| | | +--------------------------------------------------------------+ | | |
| | +------------------------------------------------------------------+ | |
| +----------------------------------------------------------------------+ |
+--------------------------------------------------------------------------+
(未准确表示反向参考的顺序。)
我想你在实践中会发现这比你想象的要快得多。和你的一样,融合是O(N)。然而,获取和设置电压是O(1),而不是O(N)。虽然在我的例子中,对象销毁是O(N)而不是O(1),但是可以通过使用哈希而不是反引用数组来实现O(1)。也就是说。作为一个阵列,它可能实际上更快。这就是Perl对CoW字符串所做的。N是融合的大小(在我们的测试用例中为4)。Perl没有您在这里处理引用的指针。@simbabque,Perl引用在功能上不是与C等语言中的指针几乎相同吗?我不太懂C来回答这个问题。它们相似,但不相同。然而,对于实际问题,我认为你应该解释一下你在这里试图实现的目标。我认为了解你为什么要这样做对我们很有用。我怀疑您是在向我们寻求实施细节方面的帮助,而向我们寻求方法方面的帮助将更有成效。我不确定你在做什么,但我很确定你走错了方向:-)与你的暗示相反,这在C中也不起作用。在这个实现中不涉及任何时髦的指针逻辑。当然,如果将它放在一个具有构造函数和all的对象中,它会更有用。。获取/设置电压为O(N),而解决方案存在于O(1)处。如果您经常获得/设置电压,这可能会产生巨大的影响。@ikegami感谢您修复内存泄漏。但不幸的是,我使用的是perl 5.6.1,我的系统上没有安装Scalar::Util。我想我们必须手动执行一些undef
s来防止像这样的旧版本出现泄漏。只安装模块会更有意义。没有理由不安装在上Solaris@ikegami更新,并保留类数据。谢谢你的评论。
use strict;
use warnings qw( all );
use feature qw( say );
use FindBin qw( $RealBin );
use lib $RealBin;
use Wire qw( );
my $o1 = Wire->new( voltage => 1 );
my $o2 = Wire->new( voltage => 2 );
my $o3 = Wire->new( voltage => 3 );
my $o4 = Wire->new( voltage => 4 );
say join " ", map $_->get_voltage(), $o1, $o2, $o3, $o4; # 1 2 3 4
$o2->fuse($o1);
$o3->fuse($o4);
$o1->fuse($o3);
say join " ", map $_->get_voltage(), $o1, $o2, $o3, $o4; # 4 4 4 4
$o1->set_voltage(5);
say join " ", map $_->get_voltage(), $o1, $o2, $o3, $o4; # 5 5 5 5
$o3->set_voltage(6);
say join " ", map $_->get_voltage(), $o1, $o2, $o3, $o4; # 6 6 6 6
package Wire;
use strict;
use warnings qw( all );
sub new {
my ($class, %args) = @_;
my $voltage = $args{voltage} // 0;
my $self = bless({}, $class);
$self->{shared_voltage} = { value => $voltage, backrefs => [] };
push @{ $self->{shared_voltage}{backrefs} }, \( $self->{shared_voltage} );
return $self;
}
sub get_voltage { $_[0]{shared_voltage}{value} }
sub set_voltage { $_[0]{shared_voltage}{value} = $_[1]; }
sub fuse {
my ($self, $new) = @_;
my $old_sv = $self->{shared_voltage}; my $old_sv_br = $old_sv->{backrefs};
my $new_sv = $new->{shared_voltage}; my $new_sv_br = $new_sv->{backrefs};
for my $backref (@$old_sv_br) {
$$backref = $new_sv;
push @$new_sv_br, $backref;
}
}
sub DESTROY {
my ($self) = @_;
@{ $self->{shared_voltage}{backrefs} } =
grep { $_ != \( $self->{shared_voltage} ) }
@{ $self->{shared_voltage}{backrefs} };
}
1;
+-$o1--+ +-Wire----------------+
| Ref -------------->| +-shared_voltage--+ | +-anon hash------+
+------+ +---------->| Reference ------------------>| +-value------+ |
| | +-----------------+ | / / / | | 4 | |
| +---------------------+ | | | | +-backrefs---+ |
| | | | | | Reference -------+
| | | | | +------------+ | |
+-$o2--+ | +-Wire----------------+ | | | +----------------+ |
| Ref -----(-------->| +-shared_voltage--+ | | | | |
+------+ | +-------->| Reference -------+ | | +------------------------+
| | | +-----------------+ | | | |
| | +---------------------+ | | | +-anon array-----+
| | | | +-->| +-0----------+ |
| | | | | | Reference -------------+
+-$o3--+ | | +-Wire----------------+ | | | +-1----------+ | |
| Ref -----(-(------>| +-shared_voltage--+ | | | | | Reference -----------+ |
+------+ | | +------>| Reference ---------+ | | +-2----------+ | | |
| | | | +-----------------+ | | | | Reference ---------+ | |
| | | +---------------------+ | | +-3----------+ | | | |
| | | | | | Reference -------+ | | |
| | | | | +------------+ | | | | |
+-$o4--+ | | | +-Wire----------------+ | +----------------+ | | | |
| Ref -----(-(-(---->| +-shared_voltage--+ | | | | | |
+------+ | | | +---->| Reference -----------+ | | | |
| | | | | +-----------------+ | | | | |
| | | | +---------------------+ | | | |
| | | | | | | |
| | | | | | | |
| | | +--------------------------------------------------------------+ | | |
| | +------------------------------------------------------------------+ | |
| +----------------------------------------------------------------------+ |
+--------------------------------------------------------------------------+