How to change a scalar link passed to a subprogram link?

I have a function to convert documents to different formats, which then calls another function based on the document type. This is pretty straight forward for everything except HTML documents that require a bit of cleanup, and that the cleanup is different depending on where it came from. Therefore, I had the idea that I could pass a link to a subroutine of the conversion function so that the caller would be able to change the HTML, sort of like (I do not work, so this is not copy and paste)

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:

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

I tried a couple of different things on these lines, but I keep getting "Using an uninitialized value in a wildcard (s ///)". Is there a way to do what I'm trying to do?

thanks

+4
source share
3 answers

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 change helper arguments, you should know that all helper arguments are passed by reference to Perl ( @_ elements @_ smoothed to subtitle arguments). So your sub converter might look like this:

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

But if you really want to work with $_ , for example, in your desired code example, you need to make _convert_html() as follows:

 sub _convert_html { my ($self, $filename, $coderef) = @_; my $html = $self->slurp($filename); $coderef->() for $html; $self->save_to_file($filename, $html); } 

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

 sub _convert_html { my ($self, $filename, $coderef) = @_; local $_ = $self->slurp($filename); $coderef->(); $self->save_to_file($filename, $_); } 
+5
source

Remember that s/// itself works on $_ , but your scalar link is passed to your callback sub as an argument and therefore is in the @_ array.

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

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

Or you could use the Perl subroutine argument alias and change it directly:

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

and then

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

(This will actually change the original string if the argument is a scalar variable and not a literal string.)

+3
source

Try the following:

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

You get a warning about an uninitialized value, because nothing was given to the substitution to work ( $_ is undefined in its scope). You need to indicate where to find its value (in @_ , as a link).

If you want to be a fantasy, you can get coderef to work with all its default arguments:

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

Source: https://habr.com/ru/post/1310058/


All Articles