如何修改传递给子例程引用的标量引用?

发布于 2024-09-02 02:21:23 字数 775 浏览 10 评论 0原文

我有一个将文档转换为不同格式的函数,然后根据类型文档调用另一个函数。除了需要进行一些清理的 HTML 文档之外,其他所有内容都非常简单,而且清理工作根据其来源而有所不同。所以我有一个想法,我可以将对子例程的引用传递给转换函数,以便调用者有机会修改 HTML,有点像这样(我不在工作,所以这不是复制粘贴的) :

package Converter;
...
sub convert
{
    my ($self, $filename, $coderef) = @_;

    if ($filename =~ /html?$/i) {
        $self->_convert_html($filename, $coderef);
    }
}

sub _convert_html
{
    my ($self, $filename, $coderef) = @_;

    my $html = $self->slurp($filename);
    $coderef->(\$html); #this modifies the html
    $self->save_to_file($filename, $html);
}

然后被调用:

Converter->new->convert("./whatever.html", sub { s/<html>/<xml>/i });

我已经尝试了几种不同的方法,但我不断收到“在替换中使用未初始化的值(s///)”。有什么办法可以做我想做的事情吗?

谢谢

I have a function to convert documents into different formats, which then calls another function based on the type document. It's pretty straight forward for everything aside from HTML documents which require a bit of cleaning up, and that cleaning up is different based on where it's come from. So I had the idea that I could pass a reference to a subroutine to the convert function so the caller has the opportunity to modify the HTML, kinda like so (I'm not at work so this isn't copy-and-pasted):

package Converter;
...
sub convert
{
    my ($self, $filename, $coderef) = @_;

    if ($filename =~ /html?$/i) {
        $self->_convert_html($filename, $coderef);
    }
}

sub _convert_html
{
    my ($self, $filename, $coderef) = @_;

    my $html = $self->slurp($filename);
    $coderef->(\$html); #this modifies the html
    $self->save_to_file($filename, $html);
}

which is then called by:

Converter->new->convert("./whatever.html", sub { s/<html>/<xml>/i });

I've tried a couple of different things along these lines but I keep on getting 'Use of uninitialized value in substitution (s///)'. Is there any way of doing what I'm trying to do?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

背叛残局 2024-09-09 02:21:23

如果是我,我会避免修改标量引用而只返回更改后的值:

sub _convert_html
{
    my ($self, $filename, $coderef) = @_;

    my $html = $self->slurp($filename);
    $html = $coderef->( $html ); #this modifies the html
    $self->save_to_file($filename, $html);
}

但是,如果您想修改子参数,则值得知道所有子参数在 Perl 中都是按引用传递的( @_ 是子调用参数的别名)。所以你的转换子可以看起来像:

sub { $_[0] =~ s/<html>/<xml>/ }

但是如果你真的想对 $_ 进行操作,就像你想要的代码示例中那样,你需要使 _convert_html() 看起来例如:

sub _convert_html
{
    my ($self, $filename, $coderef) = @_;

    my $html = $self->slurp($filename);

    $coderef->() for $html;

    $self->save_to_file($filename, $html);
}

for 是正确本地化 $_ 的简单方法。您还可以这样做:

sub _convert_html
{
    my ($self, $filename, $coderef) = @_;

    local $_ = $self->slurp($filename);

    $coderef->();

    $self->save_to_file($filename, $_);
}

If it were me, I would avoid modifying the scalar ref and just return the changed value:

sub _convert_html
{
    my ($self, $filename, $coderef) = @_;

    my $html = $self->slurp($filename);
    $html = $coderef->( $html ); #this modifies the html
    $self->save_to_file($filename, $html);
}

However, if you want to modify a sub's arguments, it is worth knowing that all sub arguments are pass-by-reference in Perl (the elements of @_ are aliased to the arguments of the sub call). So your conversion sub can look like:

sub { $_[0] =~ s/<html>/<xml>/ }

But if you really want to operate on $_, like you have in your desired code example, you need to make _convert_html() look like:

sub _convert_html
{
    my ($self, $filename, $coderef) = @_;

    my $html = $self->slurp($filename);

    $coderef->() for $html;

    $self->save_to_file($filename, $html);
}

The for is an easy way to properly localize $_. You can also do:

sub _convert_html
{
    my ($self, $filename, $coderef) = @_;

    local $_ = $self->slurp($filename);

    $coderef->();

    $self->save_to_file($filename, $_);
}
歌入人心 2024-09-09 02:21:23

请记住,s/// 本身对 $_ 进行操作,但是您的标量引用将作为参数传递到您的回调子中,因此位于 >@_ 数组。

因此,您可以将回调 sub 更改为如下所示:

sub { my ( $ref ) = @_; $ref =~ s/<html>/<xml>/i }

或者,您可以利用 Perl 子例程参数的别名性质,并直接修改它:

sub _convert_html { 
    ...
    $coderef->( $html );
}

然后

sub { $_[0] =~ s/<html>/<xml>/i }

(这实际上会修改原始字符串,只要参数是标量变量而不是文字字符串。)

Remember that an s/// by itself operates on $_, but your scalar reference is being passed into your callback sub as an argument, and is therefore in the @_ array.

So you can just change your callback sub to something like this:

sub { my ( $ref ) = @_; $ref =~ s/<html>/<xml>/i }

Or, you could take advantage of the aliased nature of Perl subroutine arguments, and modify it directly:

sub _convert_html { 
    ...
    $coderef->( $html );
}

and then

sub { $_[0] =~ s/<html>/<xml>/i }

(This will actually modify the original string, as long as the argument is a scalar variable and not a literal string.)

眼中杀气 2024-09-09 02:21:23

试试这个:

Converter->new->convert("./whatever.html", sub { ${$_[0]} =~ s/<html>/<xml>/i; });

您收到未初始化值警告,因为没有为替换提供任何可操作的内容($_ 在其范围内未定义)。您需要告诉它在哪里可以找到它的值(在 @_ 中,作为参考)。

如果你想变得更奇特,你可以让 coderef 默认对其所有参数进行操作:

sub { map { $_ =~ s/<html>/<xml>/i } @_ }

Try this:

Converter->new->convert("./whatever.html", sub { ${$_[0]} =~ s/<html>/<xml>/i; });

You are getting an uninitialized value warning because the substitution isn't being given anything to operate on ($_ is undefined in its scope). You need to tell it where to find its value (in @_, as a reference).

If you want to be fancy you could make the coderef operate on all its args by default:

sub { map { $_ =~ s/<html>/<xml>/i } @_ }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文