如果你在匿名 sub 中调用 shift 会发生什么?

What happens if you call shift inside an anonymous sub?

首先,如果这个问题不恰当,我们深表歉意;我实际上并不知道很多 perl。

我正在尝试调试一些现有代码,这些代码应该将成绩从我们名为 WeBWorK 的在线作业系统发送到 LMS。我 运行 遇到了一个奇怪的错误,我认为某些东西没有正确初始化,或者可能不正确 class。我怀疑问题可能出在这里:

sub go {
    my $self = shift;
    my $r = $self->r;
    my $ce = $r->ce;

    # If grades are begin passed back to the lti then we peroidically
    # update all of the grades because things can get out of sync if
    # instructors add or modify sets.
    if ($ce->{LTIGradeMode}) {

      my $grader = WeBWorK::Authen::LTIAdvanced::SubmitGrade->new($r);

      my $post_connection_action = sub {
        my $grader = shift;

        # catch exceptions generated during the sending process
        my $result_message = eval { $grader->mass_update() };
        if ($@) {
          # add the die message to the result message
          $result_message .= "An error occurred while trying to update grades via LTI.\n"
        . "The error message is:\n\n$@\n\n";
          # and also write it to the apache log
          $r->log->error("An error occurred while trying to update grades via LTI: $@\n");
        }
      };
      if (MP2) {
        $r->connection->pool->cleanup_register($post_connection_action, $grader);
      } else {
        $r->post_connection($post_connection_action, $grader);
      }
    }
... # a bunch of other stuff happens in the "go" sub

我有点怀疑问题出在 $grader 变量上;特别是,我不知道 my $grader = shift; 在匿名 sub 中做了什么。就像,如果 sub 有一个名字,那么 shift 给出传递给 sub 的第一个参数会更清楚。但由于是匿名的,我不知道它认为它的论点是什么。

此外,我不太确定为什么需要该行。就像,从我的谷歌搜索中,我了解到匿名子的要点是将周围环境中的所有变量都保留在范围内。那么,为什么我们首先需要在匿名 sub 中重新定义 $grader

感谢您帮助 perl 新手! :)

匿名潜艇在这方面没有什么特别之处。

my $cr = sub {
   my $arg = shift;
   say $arg;
};

$cr->("foo");   # Prints "foo"
$cr->("bar");   # Prints "bar"

在您的情况下,您将 $post_connection_action$grader 传递给 cleanup_registerpost_connection,期望它会调用 &$post_connection_action$grader 作为第一个参数。期望是否正确取决于cleanup_registerpost_connection的实现,我对此一无所知。


请注意,此处出现了另一种解决方案。当评估 sub 运算符时,Subs 可以访问范围内的词法。

my $prefix = "> ";
my $cr = sub {
   my $arg = shift;
   say "$prefix$arg";   # Captures $prefix from sub{} scope.
};

$cr->("foo");           # Prints "> foo"

即使捕获的词法在调用 sub 时不再存在,上述也是正确的。

my $cr;
{
   my $prefix = "> ";
   $cr = sub {
      my $arg = shift;
      say "$prefix$arg";   # Captures $prefix from sub{} scope.
   };
}                          # $prefix would normally stop existing here.

$cr->("foo");              # Prints "> foo"

这意味着您不需要将 $grader 作为参数传递。它可以简单地被捕获。只需省略 my $grader = shift;(并且不要将 $grader 传递给 cleanup_registerpost_connection).