Реализация макроса или оператора Perl "подразумевает"

(Я читал Написание макроса на 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?

Почему вы используете Perl 5.18 (с 2013 года)? Я бы рекомендовал обновиться до последней версии 5.40.

Håkon Hægland 29.07.2024 14:16

Ярлык функции работает нормально, это ваш код вне функции, который выдает ошибку.

TLP 29.07.2024 14:42

Если вы хотите избежать ошибок при составлении произвольного кода вне функции, вам придется побаловаться no warnings прагмой, eval или ссылками на код (например, sub { $a eq '@' }).

TLP 29.07.2024 14:45
$a//"" eq '@' если это эстетически приемлемо (может потребоваться скобки перед выражением eq)
mpapec 29.07.2024 19:11

@mpapec Я тоже об этом подумал, проблема в том, что это работает для этого конкретного случая, но не для произвольного кода. И вам нужны скобки.

TLP 29.07.2024 19:19
defined($a) ? $a eq '@' : 1 и похоже на оригинал 🙈
mpapec 29.07.2024 19:47

@HåkonHægland Это имеет отношение к вопросу или ответу? Если да, то ты должен был сказать. В противном случае: Потому что код должен работать и в более новых версиях Perl; обратное может быть неверным.

U. Windl 01.08.2024 11:50

@TLK Кажется, вы пропустили краткое содержание: конечно, при реализации как функции параметры будут оцениваться. Поэтому я просил реализацию «макро» или «оператора».

U. Windl 01.08.2024 11:51

@mpapec Да, defined($a) ? $a eq '@' : 1 можно было бы так же, как !defined(@a) || $a eq '@', но мне хотелось немного «синтаксического сахара».

U. Windl 01.08.2024 11:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
104
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам придется отложить вычисление выражения до входа в подпрограмму или скрыть ошибки. Для произвольного кода скрыть ошибки может быть сложно. Но отсрочка оценки с помощью ссылки на код будет работать:

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 29.07.2024 16:09

@simbabque В этом может быть какая-то магия, но я, кажется, помню, что невозможно пропустить ключевое слово sub без использования встроенных операторов (например, карты, сортировки и т. д.).

TLP 29.07.2024 17:27
Metacpan.org/dist/List-MoreUtils/source/lib/List/MoreUtils/… делает это с одним сабвуфером, но я думаю, что это работает только с одним, а не с двумя.
simbabque 29.07.2024 18:17

Какой это саб? Вы связали весь модуль

TLP 29.07.2024 19:17

если второй аргумент в прототипе может быть функцией и согласованность не важна, передайте первый аргумент как bool, так как он всегда требует оценки, в отличие от второго.

mpapec 29.07.2024 20:08

@TLP, извини. у большинства из них один аргумент кода, например, all вверху. Но опять же, это не позволяет использовать две сабвуферы.

simbabque 30.07.2024 13:14

@simbabque, я понимаю. Я никогда особо не разбирался в прототипах. В большинстве случаев отсутствие ключевого слова sub приведет к созданию хэш-ссылки.

TLP 30.07.2024 13:58

@TLP, возможно, вас заинтересует мой ответ.

ikegami 31.07.2024 03:57

@ikegami Выглядит интересно, но, к сожалению, я этого не понимаю.

TLP 31.07.2024 16:53

Это способ подключения к парсеру Perl для предоставления именованного инфиксного оператора, так что 1. вы можете буквально сделать EXPR1 implies EXPR2 и 2. вы получите короткое замыкание.

ikegami 31.07.2024 17:00

ИМХО, не стоит затягивать с оценкой первого параметра.

U. Windl 01.08.2024 12:03
Ответ принят как подходящий

Вы можете использовать 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

Это соответствует элегантному квалификатору. Очень хорошо.

TLP 31.07.2024 18:16

Да, неплохо, но вам нужно хорошо знать внутренности Perl. Разве не было бы неплохо, если бы я мог указать в прототипе функции, что конкретный параметр не оценивается (в смысле eval { ... }, а не в смысле eval "..."), и вы могли бы затем оценить разобранное выражение в подпрограмме, когда это необходимо?

U. Windl 01.08.2024 12:01

Другие вопросы по теме