我有一个不可思议的问题。我在Perl中使用匿名函数创建了一个简单序列。
sub{($data, sub{($data, sub{($data, sub{($data, empty)})})})};
它可以工作,但是我厌倦了实现尾部优化并得到一些奇怪的行为。例。下面的iter函数有效。
sub iter {
my ($func, $seq) = @_;
my ($data, $next) = $seq->();
if (defined $data) {
$func->($data);
@_ = ($func, $next);#This @_ update works fine
goto &iter;
}
}
此iter的实现失败。
sub iter {
my ($func, $seq) = @_;
my ($data, $next) = $seq->();
if (defined $data) {
$func->($data);
$_[1] = $next; #This @_ update fails
goto &iter;
}
}
@_的两个更新都会为@_产生相同的值,但是代码继续执行时的行为会有所不同。要了解我在说什么,请尝试运行下面的完整代码。
#! /usr/bin/env perl
package Seq;
use 5.006;
use strict;
use warnings;
sub empty {
sub{undef};
}
sub add {
my ($data, $seq) = @_;
sub{($data, $seq)};
}
sub iter {
my ($func, $seq) = @_;
my ($data, $next) = $seq->();
if (defined $data) {
$func->($data);
@_ = ($func, $next);#This works fine
#$_[1] = $next; #This fails
goto &iter;
}
}
sub smap {
my ($func, $seq) = @_;
my ($data, $next) = $seq->();
if (defined $data) {
sub{($func->($data), Seq::smap($func, $next))};
}else {
empty();
}
}
sub fold {
my ($func, $acc, $seq) = @_;
my ($data, $next) = $seq->();
if (defined $data) {
@_ = ($func, $func->($acc, $data), $next);
goto &Seq::fold;
}else {
$acc;
}
}
1;
package main;
use warnings;
use strict;
use utf8;
use List::Util qw(reduce);
my $seq =
reduce
{Seq::add($b, $a)}
Seq::empty,
(4143, 1234, 4321, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Seq::iter(sub{my ($data) = @_; STDOUT->print("$data\n")}, $seq);
my $seq2 = Seq::smap(sub{my ($data) = @_; $data * 2}, $seq);
STDOUT->print("\n\n");
Seq::iter(sub{my ($data) = @_; STDOUT->print("$data\n")}, $seq2);
STDOUT->print("\n\n");
my $ans = Seq::fold(sub{my ($acc, $data) = @_; $acc + $data}, 0, $seq);
my $ans2 = Seq::fold(sub{my ($acc, $data) = @_; $acc + $data}, 0, $seq2);
STDOUT->print("$ans\n");
STDOUT->print("$ans2\n");
exit (0);
该代码应适用于iter的两个示例,但不适用于任何示例。
答案 0 :(得分:2)
写入$_[1]
会写入传递给子级的第二个标量。
$ perl -E'$x = "abc"; say $x; sub { $_[0] = "def"; say $_[0]; }->($x); say $x;'
abc
def
def
因此,您正在破坏调用方的变量。分配给@_
会替换其中包含的标量,而不是写入标量。
$ perl -E'$x = "abc"; say $x; sub { @_ = "def"; say $_[0]; }->($x); say $x;'
abc
def
abc
您可以使用splice
替换特定元素。
$ perl -E'$x = "abc"; say $x; sub { splice(@_, 0, 1, "def"); say $_[0]; }->($x); say $x;'
abc
def
abc
迭代器用尽时返回一个空列表要方便得多。对于初学者,它允许他们返回undef
。
此外,我将使用更快的循环删除昂贵的递归调用。由于上述更改,可以使这些循环特别简单。
该模块变为:
package Seq;
use strict;
use warnings;
sub empty { sub { } }
sub add {
my ($data, $seq) = @_;
return sub { $data, $seq };
}
sub iter {
my ($func, $seq) = @_;
while ( (my $data, $seq) = $seq->() ) {
$func->($data);
}
}
sub smap {
my ($func, $seq) = @_;
if ( (my $data, $seq) = $seq->() ) {
return sub { $func->($data), smap($func, $seq) };
} else {
return sub { };
}
}
sub fold {
my ($func, $acc, $seq) = @_;
while ( (my $data, $seq) = $seq->() ) {
$acc = $func->($acc, $data);
}
return $acc;
}
1;
此外,出于速度原因,请替换
sub { my ($data) = @_; $data * 2 }
sub { my ($acc, $data) = @_; $acc + $data }
使用
sub { $_[0] * 2 }
sub { $_[0] + $_[1] }