/ / Perl: Syntaktický cukor pre posledné argumenty? - perl, syntax, programovacie jazyky, lambda, uzávierky

Perl: Syntaktický cukor pre ďalšie argumenty Coderef? - Perl, syntax, programovacie jazyky, lambda, uzávery

Pomocou sub prototypov môžeme definovať naše vlastné subs, ktoré vyzerajú ako mapa alebo grep. To znamená, že prvý argument coderef má kratšiu syntax ako normálny anonymný sub. Napríklad:

sub thunked (&) { $_[0] }

my $val = thunked { 2 * 4 };

Funguje to tu skvele, pretože prvým argumentom je coderef. Pre posledné argumenty to však nebude správne analyzovať.

Urobil som with sub určený na vylepšenie písania kódu GTK2. Má to vyzerať takto (nevyskúšané, pretože ide o hypotetický kód):

use 5.012;
use warnings;

use Gtk2 "-init";

sub with ($&) {
local $_ = $_[0];
$_[1]->();
$_;
}

for (Gtk2::Window->new("toplevel")) {
$_->set_title("Test Application");
$_->add(with Gtk2::VBox->new {
my $box = $_;
$box->add(Gtk2::Button->new("Button $_")) for (1..4);
});
$_->show_all;
}
Gtk2->main;

To nefunguje, pretože with je potrebné brať blok ako prvý argument, aby pekná syntax fungovala. Existuje nejaký spôsob, ako to dotiahnuť?

odpovede:

6 pre odpoveď č. 1

Modul Devel :: Vyhlásiť obsahuje nástroje na relatívne bezpečné rozšírenie Perlovej syntaxe.

Pomocou Devel :: Declare by ste vytvorili háčik na with token, ktorý zastaví syntaktický analyzátor, keď dosiahne dané slovo. Odtiaľ máte kontrolu nad syntaktickým analyzátorom a môžete čítať ďalej, kým sa nedostanete k { symbol. V tom okamihu máte, s čím musíte pracovať, takže ho prepíšete do platného Perlu a odovzdáte späť parseru.

v súbore With.pm:

package With;
use warnings;
use strict;
use Devel::Declare;

sub import {
my $caller = caller;
Devel::Declare->setup_for (
$caller => {with => {const => &parser}}
);
no strict "refs";
*{$caller."::with"} = sub ($&) {
$_[1]() for $_[0];
$_[0]
}
}

our $prefix = "";
sub get {substr Devel::Declare::get_linestr, length $prefix}
sub set {       Devel::Declare::set_linestr $prefix . $_[0]}

sub parser {
local $prefix = substr get, 0, length($_[0]) + $_[1];
my $with = strip_with();
strip_space();
set "scalar($with), sub " . get;
}

sub strip_space {
my $skip = Devel::Declare::toke_skipspace length $prefix;
set substr get, $skip;
}

sub strip_with {
strip_space;
my $with;
until (get =~ /^{/) {
(my $line = get) =~ s/^([^{]+)//;
$with .= $1;
set $line;
strip_space;
}
$with =~ s/s+/ /g;
$with
}

a používať ho:

use With;

sub Window::add {say "window add: ", $_[1]->str}
sub Window::new {bless [] => "Window"}
sub Box::new    {bless [] => "Box"}
sub Box::add    {push @{$_[0]}, @_[1..$#_]}
sub Box::str    {"Box(@{$_[0]})"}
sub Button::new {"Button($_[1])"}

with Window->new {
$_->add(with Box->new {
for my $num (1 .. 4) {
$_->add(Button->new($num))
}
})
};

Ktoré výtlačky:

pridať okno: Box (Tlačidlo (1) Tlačidlo (2) Tlačidlo (3) Tlačidlo (4))

Úplne iným prístupom by bolo preskočiť with kľúčové slovo a napíš rutinu na generovanie podprogramov konštruktora:

BEGIN {
for my $name (qw(VBox)) { # and any others you want
no strict "refs";
*$name = sub (&@) {
use strict;
my $code = shift;
my $with = "Gtk2::$name"->new(@_);
$code->() for $with;
$with
}
}
}

a potom by váš kód mohol vyzerať

for (Gtk2::Window->new("toplevel")) {
$_->set_title("Test Application");
$_->add(VBox {
my $box = $_;
$box->add(Gtk2::Button->new("Button $_")) for (1..4);
});
$_->show_all;
}

5 pre odpoveď № 2

Jedným zo spôsobov, ako sa s tým vyrovnať, je pridať pomerne zbytočné kľúčové slovo:

sub perform(&) { $_[0] }

with GTK2::VBox->new, perform { ... }

kde perform je skutočne iba cukrovejšou alternatívou k sub.

Ďalšou možnosťou je písať a Devel :: Vyhlásiť filter alebo a Syntax :: Kľúčové slovo :: plugin implementovať svoje with, pokiaľ máte nejaký spôsob, ako zistiť, kedy ste skončili s analýzou with argument a pripravený začať analyzovať blok - vyvážené zátvorky by stačili (urobila by to aj úvodná zložená zátvorka, ale potom by sa hashy stali problémom). Potom by ste mohli podporiť niečo ako

with (GTK2::VBox->new) { ... }

a nechajte filter prepísať na niečo podobné

do {
local $_ = GTK2::VBox->new;
do {
...;
};
$_;
}

ktorý, ak funguje, má výhodu v tom, že v skutočnosti nevytvára sub, a teda do toho nezasahuje @_, returna niekoľko ďalších vecí. Dve vrstvy do-age Myslím si, že sú nevyhnutné pre inštaláciu EndOfScope zaháknite na správne miesto.

Zjavnou nevýhodou je, že to jezložité, je to chlpaté a je to zdrojový filter (aj keď je to krotký), čo znamená, že existujú problémy, ktoré musíte vyriešiť, ak chcete, aby bol akýkoľvek kód, ktorý ho používa, vôbec laditeľný.