(Я читал Написание макроса на Perl, но все равно нужны инструкции)
У Эйфеля есть оператор implies (Импликационный логический оператор, см. «8.5.20 Синтаксис: Операторы» в ECMA-367, 2-е издание), т.е.
а
impliesб
значение
notаorб
Итак, первая попытка заключалась в использовании
# a implies b (a --> b)
sub implies($$)
{
return !$_[0] || $_[1];
}
Однако это функция, а не оператор. В частности, короткая оценка не удалась для таких случаев, как
implies(defined($a), $a eq '@')
(что приводит к «Использованию неинициализированного значения $a в строке eq в ...»).
Итак, вопрос (для Perl 5.18.2): Есть ли элегантный способ добавить такой «оператор» в Perl?
Ярлык функции работает нормально, это ваш код вне функции, который выдает ошибку.
Если вы хотите избежать ошибок при составлении произвольного кода вне функции, вам придется побаловаться no warnings прагмой, eval или ссылками на код (например, sub { $a eq '@' }).
$a//"" eq '@' если это эстетически приемлемо (может потребоваться скобки перед выражением eq)
@mpapec Я тоже об этом подумал, проблема в том, что это работает для этого конкретного случая, но не для произвольного кода. И вам нужны скобки.
defined($a) ? $a eq '@' : 1 и похоже на оригинал 🙈
@HåkonHægland Это имеет отношение к вопросу или ответу? Если да, то ты должен был сказать. В противном случае: Потому что код должен работать и в более новых версиях Perl; обратное может быть неверным.
@TLK Кажется, вы пропустили краткое содержание: конечно, при реализации как функции параметры будут оцениваться. Поэтому я просил реализацию «макро» или «оператора».
@mpapec Да, defined($a) ? $a eq '@' : 1 можно было бы так же, как !defined(@a) || $a eq '@', но мне хотелось немного «синтаксического сахара».





Вам придется отложить вычисление выражения до входа в подпрограмму или скрыть ошибки. Для произвольного кода скрыть ошибки может быть сложно. Но отсрочка оценки с помощью ссылки на код будет работать:
use strict;
use warnings;
sub implies {
return !($_[0]->()) || $_[1]->();
}
print implies(sub { defined($a) }, sub { $a eq '@' });
Будет работать так, как ожидалось. Ошибки не будет, поскольку вторая ссылка на код не выполняется из-за короткого замыкания оператора ||. Возможно, это не элегантно, но я подозреваю, что это настолько элегантно, насколько это возможно, не усложняя значительно.
Это также может работать с eval. Тогда оценка будет зависеть от того, правильно ли находится переменная в области видимости.
sub implies {
return !(eval $_[0]) || eval $_[1];
}
print implies('defined($a)', q($a eq '@'));
Я полагаю, можно было бы сделать sub implies(&&) { ... } и implies { define $a} => { $a eq '@' } или что-то в этом роде? Не знаю, как сделать два блока, следующих друг за другом.
@simbabque В этом может быть какая-то магия, но я, кажется, помню, что невозможно пропустить ключевое слово sub без использования встроенных операторов (например, карты, сортировки и т. д.).
Какой это саб? Вы связали весь модуль
если второй аргумент в прототипе может быть функцией и согласованность не важна, передайте первый аргумент как bool, так как он всегда требует оценки, в отличие от второго.
@TLP, извини. у большинства из них один аргумент кода, например, all вверху. Но опять же, это не позволяет использовать две сабвуферы.
@simbabque, я понимаю. Я никогда особо не разбирался в прототипах. В большинстве случаев отсутствие ключевого слова sub приведет к созданию хэш-ссылки.
@TLP, возможно, вас заинтересует мой ответ.
@ikegami Выглядит интересно, но, к сожалению, я этого не понимаю.
Это способ подключения к парсеру Perl для предоставления именованного инфиксного оператора, так что 1. вы можете буквально сделать EXPR1 implies EXPR2 и 2. вы получите короткое замыкание.
ИМХО, не стоит затягивать с оценкой первого параметра.
Вы можете использовать XS::Parse::Infix::FromPerl.
Он обеспечивает способ подключения к анализатору Perl для предоставления именованного инфиксного оператора. Так,
EXPR1 implies EXPR2 иМодуль Pragma: (Этот эффект имеет лексическую область видимости, как use strict;.)
package Syntax::Feature::Implies;
# Usage: `use syntax qw( implies );`
# Provides: `EXPR1 implies EXPR2`
use strict;
use warnings;
use Optree::Generate qw( newLOGOP newUNOP OP_OR OP_NOT );
use XS::Parse::Infix::FromPerl qw( register_xs_parse_infix XPI_CLS_LOGICAL_OR_MISC );
my $hintkey = __PACKAGE__;
sub import { $^H{ $hintkey } = 1; }
sub unimport { $^H{ $hintkey } = 0; }
*install = \&import; # For syntax.pm
*uninstall = \&unimport; # For syntax.pm
register_xs_parse_infix(
implies => (
cls => XPI_CLS_LOGICAL_OR_MISC, # Same precedence as `||`.
permit_hintkey => $hintkey,
new_op => sub {
#my ( $flags, $lhs, $rhs, $parsedata, $hookdata ) = @_;
return newLOGOP( OP_OR, 0,
newUNOP( OP_NOT, 0, $_[1] ),
$_[2],
);
},
)
);
1;
Тестовый скрипт:
#!/usr/bin/perl
use strict;
use warnings;
use feature qw( say );
use syntax qw( implies ); # Or use Syntax::Feature::Implies;
for my $p ( 0 .. 1 ) {
for my $q ( 0 .. 1 ) {
my $rhs_evaluated = 0;
my $r = $p implies do { ++$rhs_evaluated; $q };
say "$p implies $q = $r rhs ".( $rhs_evaluated ? "" : "not " )."evaluated";
}}
Выход:
0 implies 0 = 1 rhs not evaluated
0 implies 1 = 1 rhs not evaluated
1 implies 0 = 0 rhs evaluated
1 implies 1 = 1 rhs evaluated
Я присвоил ему тот же приоритет, что и || (непроверено), но это можно изменить.
cls
Тот же приоритет, что и
XPI_CLS_LOGICAL_AND_MISC&&XPI_CLS_LOGICAL_OR_MISC||, ^^, //XPI_CLS_LOGICAL_AND_LOW_MISCandXPI_CLS_LOGICAL_OR_LOW_MISCor, xorЭто соответствует элегантному квалификатору. Очень хорошо.
Да, неплохо, но вам нужно хорошо знать внутренности Perl. Разве не было бы неплохо, если бы я мог указать в прототипе функции, что конкретный параметр не оценивается (в смысле eval { ... }, а не в смысле eval "..."), и вы могли бы затем оценить разобранное выражение в подпрограмме, когда это необходимо?
Почему вы используете Perl 5.18 (с 2013 года)? Я бы рекомендовал обновиться до последней версии 5.40.