2010-05-17 2 views
4

У меня есть функция преобразования документов в разные форматы, которые затем вызывает другую функцию на основе документа типа. Это довольно прямолинейно для всего, кроме документов 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 ///). Есть ли способ сделать то, что я пытаюсь сделать?

Благодаря

+1

Перед тем, как прочитать любой из следующих ответов: Вы можете попробовать добавить некоторые операторы печати на каждом уровне подпрограммы, чтобы увидеть, если то, что вы получаете в качестве аргументов действительно соответствует тому, что вы думаете, должно быть. ** Подсказка: заявление печати внутри подстановки coderef должно привести вас к ответу. ** – Ether

ответ

5

Если бы это было, я бы избежать изменения скалярного реф и просто возвращает измененное значение:.

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); 
} 

Однако, если вы хотите для изменения аргументов sub, стоит знать, что все вспомогательные аргументы передаются по ссылке в 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); 
} 

The for - это простой способ правильно локализовать $_. Вы также можете сделать:

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

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

    $coderef->(); 

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

Спасибо за подробное объяснение :-) – Mark

+2

Это много копий: сначала в стек аргументов, затем измените его и скопируйте обратно в стек аргументов. Это может повредить, если вам приходится обрабатывать много, много страниц. –

+1

Обратите внимание, что если скорость важна, 'local' быстрее, чем' for' с одним элементом –

2

Попробуйте это:

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

Вы получаете предупреждение о неинициализированном значении, поскольку замена не дают ничего, чтобы работать на ($_ не определен в его объеме). Вы должны сказать, где найти его значение (в @_, в качестве справки).

Если вы хотите быть фантазиями вы могли бы сделать coderef работать на все свои аргументы по умолчанию:

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

Для примера кода карты мне интересно, каким бы я хотел получить возвращаемое значение. :) –

+0

@brian: действительно; обычно я бы написал конверсию для возврата нового значения (ов) вместо использования ссылок. – Ether

3

Помните, что s/// самого по себе действует на $_, но ваша ссылка скаляра передаются в обратный вызов sub как аргумент, и поэтому находится в массиве @_.

Таким образом, вы можете просто изменить обратный вызов к югу, чтобы что-то вроде этого:

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

Или, вы можете воспользоваться совмещенной природой аргументов подпрограммы Perl, и изменить его непосредственно:

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

, а затем

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

(Это будет на самом деле изменить исходную строку, а л Онг в качестве аргумента является скалярной переменной, а не символьная строка)

 Смежные вопросы

  • Нет связанных вопросов^_^