【问题标题】:How to enable dots (.) in subroutine names?如何在子例程名称中启用点 (.)?
【发布时间】:2014-05-28 18:07:54
【问题描述】:

我正在使用自动加载创建具有动态访问器的模块。被调用的方法名称中将包含一个或多个点。使用示例:

use MyModule;
my $var=MyModule->a.method.get;
#MyModule::a.method.get() is now defined 

这些点似乎是子程序名称中的非法字符:

#!/usr/bin/perl
use warnings;
use strict;

sub mr.s
{
        print "OK\n";
}
mr.s();

放弃

Illegal declaration of subroutine main::mr at main.pl line 5

那么,如何在子例程名称中启用点?

我了解,至少有一些受访者可能会不鼓励使用自动加载,并尝试更改合法的子例程名称,而不是提供更好的做法示例。我不反对这些答案,也不认为它们没有任何价值。不过,请尝试为我最初的问题提供答案。

编辑: 澄清一下,最终目标是一个模块,其中,当调用未定义的子例程时,它是根据其名称动态定义的。为了实现这一点,我的意思是使用 AUTOLOAD,其使用方式类似于 this tutorial 中的使用方式。

【问题讨论】:

  • 你的问题和答案与自动加载无关;也许您的意思与我们预期的不同?
  • 您的文字似乎暗示了对象方法,但您的使用示例是针对类方法的;你能澄清一下吗?
  • 我已经编辑了一些关于自动加载的说明。我目前的意思是只调用类方法并使用包变量。在我的问题中,我真的没有看到任何“对象”含义,你能指出它们吗?
  • “访问器”通常不是用于类方法的术语(至少在 perl OO 世界中)
  • @ysth,类可以有访问类属性的访问器。

标签: perl subroutine


【解决方案1】:

你说得对,子程序名称中的点是非法的。这与自动加载无关,它们只是非法的。一个点用于字符串连接,所以你的例子

MyModule->a.method.get

实际上会被解释为

MyModule->a() . method() . get()

【讨论】:

  • 我害怕那个。调用特定模块的方法时是否可以禁用此连接?
  • 有一些丑陋和贬低的技巧,如源过滤器,但仅在调用特定模块时强制执行这种语义更改可能行不通。除此之外,将 Perl 用作 Perl,不要试图强制执行其他语言的外观。
  • 必须将 perl 分叉到不同的解释器并重写规范。
【解决方案2】:

你不能那样做,不能像普通方法一样调用它们 [*]。

您的选择是放弃 .并改用 _ 或其他东西,或者不创建访问器子程序,而是使用一个通用的 get() 函数,该函数将属性名称作为参数。

( * 脚注:您可以创建此类方法并间接调用它们,如下所示:

package Foo;
use strict;
use warnings;
BEGIN {
    no strict 'refs';
    *{'foo.bar'} = sub {
        use strict;
        return 'foo.bar value';
    }
}


package main;
use strict;
use warnings;
my $method = 'foo.bar';
my $value = Foo::->$method;

但这会导致比你想要的更头痛。)

【讨论】:

  • 只有在我事先知道方法名称的情况下,脚注实现才会起作用。有没有办法解决这个问题?
  • @Worse_Username, AUTOLOAD,但你显然忽略了MyModule->do("a.method.get") 更简单的观点。
  • @Worse_Username,你错了。 AUTOLOAD 根本不在乎这个名字。 perl -E"sub AUTOLOAD { say $AUTOLOAD =~ s/^.*:://r } my $method = 'a.b'; __PACKAGE__->$method();" 但就像我说的,下面的要简单得多:perl -E"sub do { say $_[1] } __PACKAGE__->do('a.b');"
  • “在调用 AUTOLOAD 之前将方法名称分配给标量字符串...”不,首先调用它是必要的。 AUTOLOAD与否仍然与您的问题无关。
  • AUTOLOAD 将接收使用的方法名称,无论它是什么。那将是您的代码中的a。如果方法在其中调用为.AUTOLOAD 将接收它。
【解决方案3】:

您为什么要尝试使用 Java(或其他)语法?在 Perl 中,它写成 MyModule->a->get。使用它。

package MyModule;

use strict;
use warnings;

sub AUTOLOAD {
   my $self = shift;

   my $name = our $AUTOLOAD =~ s/^.*:://r;
   my $path = ref($self) ? $self->{path}.".".$name : $name;

   if (is_path_method($path)) {
      return call_method($path);
   } else {
      return MyModule->___new(path => $path);
   }
}

sub ___new {
   my $class = shift;
   return bless({ @_ }, $class);
}

sub DESTROY { }

请注意,如果尝试访问代理的 AUTOLOADcanDESTROYDOESimportisa___newVERSION,这将失败。

如果您可以拥有同名的方法和属性,请使用MyModule->a->get->(),您可以通过重载&{}来做到这一点。

【讨论】:

    【解决方案4】:

    根据http://perldoc.perl.org/perldata.html#Identifier-parsing,我没有看到'mr.s' 在 utf8 或非 utf8 源解析下验证为标识符。因此,它不是一个有效的子例程名称。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 2012-09-20
      • 1970-01-01
      • 1970-01-01
      • 2014-11-15
      • 2015-06-18
      • 2010-12-27
      相关资源
      最近更新 更多