2016-11-23 2 views
2

В Perl можно ли взять ссылку на ссылку на оператора (например, eq или gt)? Или это невозможно, потому что они не являются субтитрами? Я бы хотел избежать переопределения операторов в качестве подмножеств, где требуется функция сравнения.Обратитесь к оператору Perl

Я применяю функцию проверки к значениям конфигурации, где проверка зависит от ключа конфигурации. Один такой контролер «сравнивается с значением другого ключа». Функция сравнения может быть определена через анонимный sub, или именованный элемент sub может быть реализован для каждого типа сравнения, но я думаю, что было бы лучше сказать compare_to("OTHER_KEY", \&eq).

EDIT: поскольку один комментатор хотел контекста, а кто-то еще проголосовал за закрытие как «слишком широкий», я буду совершенно ясно, что вопрос не «как написать проверку конфигурации». Это касается только ссылок на операторов: возможно или нет, какие оговорки применяются?

+0

Как будет использоваться функция сравнения? –

+2

Должен ли я считать, что это означает «нет, но, может быть, вы можете решить его каким-то другим способом»? Я тоже заинтересован в ответе. Я обновил вопрос. –

ответ

4

Сравнения как eq и gt подпадают под особую категорию основных функций, где вы не можете создавать ссылки на них. Это подробно описано в документации здесь http://perldoc.perl.org/CORE.html

Таким образом, похоже, что вы не можете делать то, что хотите. Я думаю, что ваши варианты включают в себя:

  • Использование анонимных подлодки или именованные подлодки, чтобы обернуть компаратор вы хотите использовать
  • Передача в строке 'eq' и делать в if в compare_to и позвонив компаратор там.
  • Вы можете сделать то, что делают некоторые другие языки, и сделать второй аргумент 1, 0 или -1 и принять это значение для gt, eq, lt, тогда, если это значение является coderef, вызовите это.
  • Возможно, существует способ psuedo-magic приблизиться к этому, используя AUTOLOAD, если вы заинтересованы в том, чтобы спуститься по этому маршруту.

Update:

Пример AUTOLOAD магии:

use feature qw(say); 

my %bin_ops = map { $_ => 1 } qw(eq ne gt lt); 
sub AUTOLOAD { 
    $AUTOLOAD =~ s/^.*:://; 
    die "..." if !$bin_ops{$AUTOLOAD}; 
    return eval "\$_[0] $AUTOLOAD \$_[1]"; 
} 

sub compare_to { 
    my ($sub, $a, $b) = @_; 
    return $sub->($a, $b); 
} 

say compare_to(\&eq, 1, 1) ? "yep" : "nope"; 
say compare_to(\&eq, 1, 0) ? "yep" : "nope"; 
say compare_to(\&gt, 1, 0) ? "yep" : "nope"; 
say compare_to(\&lt, 1, 0) ? "yep" : "nope"; 

Забегая:

> perl tmp/test_coreref.pl 
yep 
nope 
yep 
nope 

Там может быть способ сделать это без использования Eval, но я пока не будем продолжать это делать дальше.

+0

Меня интересовал бы какой-либо магический подход ради интеллектуального любопытства, но я вряд ли реально его использовал (было бы значительно менее понятно, чем просто обертывание оператора). –

+2

Использование 'for (qw (eq ne gt lt)) {eval" sub $ _ {\ $ _ [0] $ _ \ $ _ [1]} "}' быстрее, чем это неприятное и незаконное использование AUTOLOAD! – ikegami

+0

Re "* Возможно, есть способ сделать это без использования eval *", вы можете запутать 'eval', используя' require', 'do' и' s /// ee', но вы все равно будете называть ' eval', необходимый шаг в генерации кода. – ikegami

4

Создание строки выполняется с использованием "abc".

Создание оператора выполняется с использованием sub { $_[0] eq $_[1] } или как eval('$_[0] eq $_[1]').

Это не дублирует оператора; они создают его.

В комментариях вы продолжаете настаивать на своем вопросе, спрашиваете, возможно ли получить ссылку на оператора без его создания (сначала «обернуть» его кодом). Очевидно, ответ отрицательный.


вопрос, с другой стороны, спрашивает, если это возможно, чтобы получить ссылку на оператора, или, скорее, eq конкретно.

можно получить ссылку на некоторые операторы, используя \&CORE::op_name[1], но не eq. Автоматическая генерация вспомогательного устройства, использующего этот синтаксис, поддерживается только для операторов, синтаксис которых может быть аппроксимирован подпрограммой с прототипом [2].

Конечно, вы можете легко создать вызываемую экземпляр оператора самостоятельно вместо того, чтобы Perl создать для Вас:

my $eq = sub { $_[0] eq $_[1] }; 

compare("OTHER_KEY", $eq)  

или

sub eq { $_[0] eq $_[1] } 

compare("OTHER_KEY", \&eq) 

Если вы хотите, чтобы избежать дублирования кода (имея кучу аналогичных подписок), вы можете использовать eval.

eval("sub $_ { \$_[0] $_ \$_[1] }") 
    for qw(eq ne gt lt); 

compare("OTHER_KEY", \&eq) 

Вы могли бы просто использовать следующее, но это было бы невероятно расточительно и опасно:

sub compare { 
    ref($_[1]) 
     ? $_[1]->($val, $_[0]) 
     : eval("\$val $_[1] \$_[0]") 
} 

compare("OTHER_KEY", "eq")` 

Если вы не хотите включать какие-либо подводные лодки, единственной альтернативой является:

sub compare { 
    ref($_[1]) ? $_[1]->($val, $_[0]) : 
    $_[1] eq "eq" ? $val eq $_[0] : 
    $_[1] eq "ne" ? $val ne $_[0] : 
    $_[1] eq "lt" ? $val lt $_[0] : 
    $_[1] eq "gt" ? $val gt $_[0] : 
    die "Bad argument"; 
} 

  1. С 5.16.

  2. Только те, для которых prototype("CORE::op_name") возвращает определенное значение. Это справедливо для некоторых именованных операторов списка (например, length) и некоторых названных унарных операторов (например, time), но это не для любых именованных двоичных операторов (например, eq).

+0

Я не должен был ограничивать мое требование «избегать переопределения» для анонимных подписчиков - явно названных работ. Именно этого я и хотел бы избежать. –

+1

Из интереса к тому, возможно ли и потому, что - IMO-дублирование отвлекает при чтении кода (и особенно неэлегантно для встроенных функций). –

+1

Как я уже сказал в этом вопросе, я подозревал, что это будет невозможно, потому что они не являются субмаринами. Поэтому ответ просто «нет». –

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