2016-02-03 3 views
8

Ich schreibe Code, wo ich eine Subroutine als Lvalue und Rvalue zum Lesen und Schreiben von Datenbankwerten verwenden. Das Problem ist, ich möchte, dass es anders reagiert, je nachdem, ob es als L-Wert oder als R-Wert verwendet wird.Überprüfen Sie, ob eine Unterroutine als Lvalue oder Rvalue in Perl verwendet wird

Ich möchte das Unterprogramm in die Datenbank schreiben, wenn es als Lvalue verwendet wird, und aus der Datenbank lesen, wenn es als Rvalue verwendet wird.

Beispiel:

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

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

Der einzige Weg, ich die Marke denken kann, diese Arbeit ist eine Möglichkeit für das Unterprogramm zu finden, zu erkennen, ob sie als L-Wert verwendet wird, oder ein R-Wert und reagiert unterschiedlich für jeden Fall.

Gibt es eine Möglichkeit, dies zu tun?

+0

Ich stimme zu, aber ich versuche, die Funktionalität einer ähnlichen API in Python für den Datenbankzugriff zu spiegeln und es scheint nur sauber aus einer UI-Perspektive mit einem Lvalue-Unterprogramm aussehen. – tjwrona1992

+0

Wenn Sie Moose verwenden, gibt es [MooseX :: LvalueAttribute] (https://metacpan.org/pod/MoseX::LvalueAttribute). – ThisSuitIsBlackNot

+2

Trotz der schlauen Idee und den Lösungen unten, muss ich sagen, dass ich [ysth] (http://stackoverflow.com/a/35184272/622310) zustimme, der vorschlägt, dass Sie einfach $ database-> record_name aufrufen sollten ($ subscript, $ value) 'um in die Datenbank zu schreiben. Dann muss Ihr Unterprogramm lediglich nach dem Vorhandensein des zweiten Parameters suchen. Etwas komplizierter als das und Sie werden Code erstellen, den niemand sonst versteht. – Borodin

Antwort

4

Die Entscheidung, wie auf verhalten, ob es genannt als L-Wert oder nicht ist eine schlechte Idee, da foo(record_name(...)) es als L-Wert nennen würde.

Stattdessen sollten Sie entscheiden, wie Sie sich verhalten sollen, ob als Lvalue verwendet wird oder nicht.

Sie können dies tun, indem Sie eine magical value zurückgeben.

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; 
} 

Ein kleiner 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. 

Ausgang:

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

Danach sehen, Sie haben gelernt, sicher, dass Sie die Idee der Verwendung eines L-Wert-Unter verlassen sollte. Es ist möglich, all diese Komplexität zu verbergen (zB durch Verwendung von sentinel), aber die Komplexität bleibt bestehen. Die Auffassungsgabe ist nicht die ganze Komplexität wert. Verwenden Sie separate Setter und Getter oder verwenden Sie stattdessen einen Accessor, dessen Rolle auf der Anzahl der übergebenen Parameter basiert ($s=acc(); vs acc($s)).

+3

Diese Antwort ist unglaublich verwirrend haha. Am Ende habe ich beschlossen, Lvalue-Subroutinen ganz zu vermeiden, aber ich bin wirklich fasziniert von der Idee, magische Werte zurückzugeben! – tjwrona1992

+0

@Borodin: Unabhängig davon, was ich selbst entschieden habe, ist dies die einzige Antwort, die wirklich die Frage beantwortet und erklärt, wie man das macht, was gefragt wurde. Ich gebe zu, dass ich es selbst nicht getestet habe, und ich verstehe die Lösung auch nicht, aber ich habe nie gewusst, dass Ikegami eine schlechte Antwort gibt, also vertraue ich dieser Lösung. – tjwrona1992

+0

@ tjwrona1992: Ein anderes Problem ist, dass es sehr umständlich wäre, eine 'lvalue'-Subroutine als Setter zu verwenden, weil die Subroutine den rvalue überhaupt nicht sieht - sie gibt nur eine Variable zurück, der der rvalue zugewiesen wird, und es sei denn, Sie geben ein Objekt mit überladener Zuweisung zurück, das nicht funktionieren wird – Borodin

0

Meiner Meinung nach waren lvalue Subroutinen in Perl eine dumme Idee. Unterstützen Sie einfach ->record_name($subscript, $value) als Setter und ->record_name($subscript) als Getter.

Das heißt, können Sie das Want Modul verwenden, wie diese

use Want; 

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

aber, dass auch diese behandeln, als ein L-Wert:

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

Wenn Sie nur Zuweisungsanweisungen speziell behandelt werden Verwenden Sie stattdessen want('ASSIGN').

+1

Wenn Sie 'will ('LVALUE')' verwenden, dann schlägt 'f (Datensatzname)' fehl (vorausgesetzt, Sie möchten den Datensatznamen an 'f' übergeben). Wenn Sie aber 'will ('ASSIGN')' verwenden, dann bricht '$ _ = 1 für Datensatzname;'. Dieser Ansatz ist inhärent fehlerhaft. – ikegami

+1

@ikegami: yup, ist es. Ich habe etwas handgeschrieben, das wie deine Antwort funktioniert hat, aber ich wusste nicht, dass es ein Modul dafür gibt. In jedem Fall sorgt das Ganze für eine sucky Schnittstelle. – ysth

+0

@ysth: 'nicht wollen ('RVALUE')' sollte gut funktionieren – Borodin

4

Für diese Situation möchten Sie vielleicht mein Sentinel Modul versuchen.

Es bietet eine Funktion, die Sie im Accessor verwenden können, um es in einen mehr get/set Stil zu verwandeln. Z.B. Sie

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; 
} 

An dieser Stelle könnten, sind die folgenden Paare von Codezeilen

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

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

Natürlich gleichwertig, wenn Sie nicht auf die spezifischen get_ und set_ Präfix Versionen der Methoden zur Verfügung zu stellen, um außerdem könnten Sie sie als Verschlüsse inline einbinden.

Weitere Informationen finden Sie in den Moduldokumenten.

Verwandte Themen