2016-02-03 26 views
8

Sto scrivendo del codice dove sto usando una subroutine come sia un lvalue che un valore rvalore per leggere e scrivere valori di database. Il problema è che voglio reagire in modo diverso in base al fatto che venga usato come un lvalue o un valore.Verificare se una subroutine viene utilizzata come lvalue o un valore in Perl

Desidero che la subroutine scriva nel database quando viene utilizzata come un lvalue e letta dal database quando viene utilizzata come valore di rvalue.

Esempio:

# Write some data 
$database->record_name($subscript) = $value; 

# Read some data 
my $value = $database->record_name($subscript); 

L'unico modo che posso pensare di fare questo lavoro è di trovare un modo per il subroutine per riconoscere se viene utilizzato come valore assegnabile o rvalue e reagiscono in modo diverso per ciascun Astuccio.

C'è un modo per farlo?

+0

Sono d'accordo, ma sto cercando di rispecchiare la funzionalità di un'API simile usata in Python per l'accesso al database e sembra semplicemente più pulito da una prospettiva UI usando una subroutine lvalue. – tjwrona1992

+0

C'è [MooseX :: LvalueAttribute] (https://metacpan.org/pod/MooseX::LvalueAttribute) se usi Moose. – ThisSuitIsBlackNot

+2

Nonostante l'idea di fantasia e le soluzioni di seguito, devo dire che sono d'accordo con [ysth] (http://stackoverflow.com/a/35184272/622310) che suggerisce di chiamare semplicemente '$ database-> record_name ($ subscript, $ value) 'per scrivere nel database. Quindi tutta la subroutine deve fare è verificare l'esistenza del secondo parametro. Qualunque cosa più elaborata di quella e creerai un codice che nessun altro comprende – Borodin

risposta

4

Decidere come comportarsi se fosse chiamato come valore assegnabile o non è una cattiva idea, dato foo(record_name(...)) lo chiamano come valore assegnabile.

Invece, si dovrebbe decidere come comportarsi sul fatto che sia utilizzato come valore lordo o meno.

È possibile farlo restituendo un magical value.

use Variable::Magic qw(cast wizard); 

my $wiz = wizard(
    data => sub { shift; \@_ }, 
    get => sub { my ($ref, $args) = @_; $$ref = get_record_name(@$args); }, 
    set => sub { my ($ref, $args) = @_; set_record_name(@$args, $$ref); }, 
); 

sub record_name :lvalue { 
    cast(my $rv, $wiz, @_); 
    return $rv; 
} 

un piccolo test:

use Data::Dumper; 

sub get_record_name { print("get: @_\n"); return "val"; } 
sub set_record_name { print("set: @_\n"); } 

my $x = record_name("abc", "def");  # Called as rvalue 

record_name("abc", "def") = "xyz";  # Called as lvalue. Used as lvalue. 

my $y_ref = \record_name("abc", "def"); # Called as lvalue. 
my $y = $$y_ref;       # Used as rvalue. 
$$y_ref = "xyz";       # Used as lvalue. 

uscita:

get: abc def 
set: abc def xyz 
get: abc def 
set: abc def xyz 

Dopo aver visto questo, hai sicuramente imparato che si dovrebbe abbandonare l'idea di utilizzare un sub lvalue. È possibile nascondere tutta quella complessità (ad esempio utilizzando sentinel), ma la complessità rimane. La fantasia non vale tutta la complessità. Utilizzare setter e getter separati o utilizzare un accessor il cui ruolo è basato sul numero di parametri passati ($s=acc(); rispetto a acc($s)).

+3

Questa risposta è incredibilmente confusa ahah. Alla fine ho deciso di andare solo evitando completamente subroutine lvalue, ma sono davvero incuriosito dall'idea di restituire valori magici! – tjwrona1992

+0

@Borodin: Indipendentemente da ciò che ho deciso di fare da solo, questa è l'unica risposta che risponde veramente alla domanda e spiega come fare ciò che è stato chiesto. Devo ammettere che non l'ho mai provato da solo, né ho compreso appieno la soluzione, ma non ho mai saputo che ikegami avrebbe dato una cattiva risposta, quindi ho fiducia che questa soluzione funzionerà. – tjwrona1992

+0

@ tjwrona1992: Un altro problema è che sarebbe molto scomodo utilizzare una subroutine 'lvalue' come setter, perché la subroutine non vede affatto il valore rval - restituisce semplicemente una variabile a cui verrà assegnato il valore rvalue, e a meno che non si restituisca un oggetto con sovraccarico assegnazione che non funzionerà – Borodin

0

A mio parere, le subroutine di lvalue in Perl erano un'idea stupida. Supporta semplicemente ->record_name($subscript, $value) come setter e ->record_name($subscript) come getter.

Detto questo, è possibile utilizzare il modulo Want, come questo

use Want; 

sub record_name:lvalue { 
    if (want('LVALUE')) { 
     ... 
    } 
    else { 
     ... 
    } 
} 

però che sarà anche trattare questo come valore assegnabile:

foo($database->record_name($subscript)); 

se desideri solo istruzioni di assegnazione da trattare specificamente , utilizzare invece want('ASSIGN').

+1

Se usi 'want ('LVALUE')', quindi 'f (record_name)' fallisce (assumendo che tu voglia passare il nome del record a 'f'). Ma se usi 'want ('ASSIGN')', quindi '$ _ = 1 per recordname;' interrompe. Questo approccio è intrinsecamente imperfetto. – ikegami

+1

@ikegami: sì, lo è. Ho codificato a mano qualcosa che ha funzionato come la tua risposta prima, ma non sapevo che esistesse un modulo per questo. In ogni caso, l'intera faccenda rappresenta un'interfaccia accattivante. – ysth

+0

@ysth: 'not want ('RVALUE')' dovrebbe funzionare bene – Borodin

4

Per questa situazione, è possibile provare il modulo Sentinel.

Fornisce una funzione che è possibile utilizzare nella funzione di accesso, per trasformarla in un approccio stile get/set. Per esempio. si potrebbe

use Sentinel qw(sentinel); 

sub get_record_name { ... } 
sub set_record_name { ... } 

sub record_name 
{ 
    sentinel get => \&get_record_name, 
      set => \&set_record_name, 
      obj => shift; 
} 

A questo punto, le seguenti coppie di righe di codice sono equivalenti

$name = $record->record_name; 
$name = $record->get_record_name; 

$record->record_name = $new_name; 
$record->set_record_name($new_name); 

Naturalmente, se non avete la necessità di fornire le specifiche get_ e set_ versioni prefissati dei metodi pure, li potresti inline come chiusure.

Vedere il modulo docs anche per ulteriori idee.

Problemi correlati