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ď č. 1Modul 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 @_
, return
a 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ý.