【问题标题】:How to get name of the called aliased subroutine?如何获取被调用的别名子程序的名称?
【发布时间】:2018-07-12 16:14:43
【问题描述】:

我如何知道哪个别名被用来调用别名子程序? caller 给出了原始的sub-name,但我希望看到调用时使用的名称。

例子:

use 5.010;
sub x_y_z {
  return ( caller(0) )[3];
}

*foo_bar_baz = \&x_y_z;

say x_y_z();        # x_y_z
say foo_bar_baz();  # x_y_z, but need foo_bar_baz

编辑以解决 XY 问题

我添加了另一个例子来展示我更深层次的意图。我想创建调度表来路由一些任务:

my $dispatch = {
  x => {
    y => {
      z => sub {
          &x_y_z;
      },
    }
  },
  a => {
    b => {
      c => {
        d => sub {
          &a_b_c_d;
        },
      }
    }
  }
}

sub foo {
  my @arg = ( split '_', ( split( '::', ( caller(0) )[3] ) )[1] );  
  return @arg;
}

*x_y_z = \&foo;
*a_b_c_d = \&foo;

如您所想,这棵树可能会长得很大。现在调度树中的许多叶子需要基本相同的子,它们的不同之处只是它们被称为(命名)的方式,我希望只有一个子并为特定任务取别名。

【问题讨论】:

  • 似乎是 XY 问题。您正在处理的更大的问题是什么?
  • @mob 编辑问题以解决 XY 问题
  • 如果您需要知道使用了哪个别名,那么您不应该使用别名。您需要为子例程添加一个参数,以传达您希望在使用的子例程别名中传递的信息。
  • 好的,但是你还没有解释为什么你需要能够区分。这应该是两个不同的子例程或带有参数的同一个子例程,例如 foo('x_y_z')foo('a_b_c_d')
  • “调用别名子问题很有趣” 不,这是一个误解。您正在尝试使用别名的名称作为附加参数,并且您的评论清楚地表明这一切已经发生,因为您试图通过调用 &a_b_c_d 来传递 @ 的当前内容来“聪明” 987654328@。不要那样做:改为编写清晰的代码。我很惊讶你没有使用goto &abcd 来搞乱调用框架。

标签: perl


【解决方案1】:

您想要做的事情在 Perl 的数据模型中根本不可能。别名只是一个别名,不是一个有自己身份的对象。

请注意,可以复制一个子程序并给它一个新名称,例如:

use Sub::Name;

*x_y_z = subname x_y_z => \&foo;

但您必须手动执行此操作。

除了堆栈跟踪之外,依赖子名并不是一个好主意。尝试在这些名称之上构建任何逻辑最终可能会导致难以调试的混乱,而不是优雅的软件。

最好将路由名称作为显式参数传递给处理函数,并创建一个辅助函数来抽象必要的管道。例如:

my %routes;

sub route {
  my ($name, $handler) = @_;
  $routes{$name} = sub { $handler->($name => @_) };
  return;
}

sub common_handler { ... }

route a_b_c => \&common_handler;
route x_y_z => \&common_handler;
route foo_bar => sub {
  my ($route) = @_;
  say "Custom handler invoked for route $route";
};

$routes{$name}->(@args);

如果绝对必要,您当然可以实现这样的route 函数,以便将处理程序安装为命名子例程。但此时您正在构建某种框架,例如 Moo(se),而不是普通的 Perl 模块。

【讨论】:

    【解决方案2】:

    你不能。 foo_bar_baz 是别名。 caller 报告声明的子例程的名称,而不是调用它的名称。请注意,并非所有子例程都有名称,也并非所有调用都是按名称命名的。 (匿名子仅作为 CODE 引用存在;它们在符号表中没有条目。任何子(无论是否命名)都可以通过引用调用。)

    也就是说,这里不需要别名。您真正想要的是 sub 应该操作的数据库、表等的额外参数。这样做的惯用方法是包装通用 sub 并通过包装器传递该信息:

    my %dispatch = (
        a => { b => { c => sub { foo('a', 'b', 'c', @_) } } },
        x => { y => { z => sub { foo('x', 'y', 'z', @_) } } },
    );
    
    $dispatch{a}{b}{c}->('foo');
    $dispatch{x}{y}{z}->('bar');
    
    sub foo {
        my $db     = shift;
        my $table  = shift;
        my $task   = shift;
        my @params = @_;
        say "$db $table $task: @params";
    }
    

    【讨论】:

    • 不明白,“这样的名字可能根本不存在”是什么意思? foo_bar_bazx_y_z 都在符号表中快乐地生活。
    • @w.k:我指的是通过CODE 参考拨打的电话。我已经修改了答案以使其更清楚。
    猜你喜欢
    • 2014-07-11
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2012-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多