2 Stimmen

Wie ruft man eine Unterfunktion auf, bei der einer Variablen bereits ein Wert zugewiesen wurde?

In Perl, when one uses the sort function with a custom comparison, the variables $a and $b are already assigned to the current pair of elements to compare, e.g. in:

@decreasing = sort { $b <=> $a } @list;

Wie kann ich andere Unterprogramme mit einer ähnlichen Funktionalität schreiben? Zum Beispiel, stellen wir uns vor, ich möchte eine Art von prozess_und_speicher-Funktion schreiben, die etwas Besonderes mit jedem Element einer Liste macht und es dann in einer Datenbank speichert; und wobei die Variable $item bereits dem aktuellen zu verarbeitenden Element zugewiesen ist. Ich würde gerne etwas wie das Folgende schreiben:

prozess_und_speicher { etwas_mit($item); } @list;

anstatt:

prozess_und_speicher { my $item = shift; etwas_mit($item); } @list;

Wie sollte ich dabei vorgehen?


UPDATE: Zur Vollständigkeit, obwohl die Antwort von flesk ohne Probleme funktioniert, um die von mir vorgenommenen Änderungen an der $item-Variablen "ordnungsgemäß" zu lokalisieren, musste ich dem Rat von Axeman folgen. In der Datei SomePackage.pm habe ich etwas Ähnliches platziert:

package SomePackage;

use strict;

require Exporter;
our @ISA = qw/Exporter/;
our @EXPORT = qw(prozess_und_speicher);

our $item;

sub import { 
  my $import_caller = caller();
  {   no strict 'refs';
    *{ $import_caller . '::item' } = \*item;
  }
  # Jetzt, ruf den Exporter auf!
  goto &{ Exporter->can( 'import' ) };
}

sub prozess_und_speicher (&@) {
  my $code = shift;
  for my $x (@_) {
    local *item = \$x;
    $code->();
    print "gespeichert: $item\n"
  }
}

1;

Dann rufe ich das von main.pl aus mit etwas Ähnlichem auf:

#!/usr/bin/perl -w

use strict;
use SomePackage;

prozess_und_speicher { print "gesehen: $item\n"; } (1, 2, 3);

Und erhalte das erwartete Ergebnis:

gesehen: 1
gespeichert: 1
gesehen: 2
gespeichert: 2
gesehen: 3
gespeichert: 3

4voto

Axeman Punkte 29362

In meiner "assoziativen Array" -Verarbeitungsbibliothek mache ich etwas Ähnliches. Der Benutzer kann die Variablen $k und $v (Schlüssel-Wert) exportieren, damit sie Dinge wie diese tun können:

each_pair { "$k => $v" } some_source_list() 

So mache ich es:

  1. Ich erkläre our ( $k, $v ) im implementierenden Paket.
  2. In import erlaube ich Paketen, diese Symbole zu exportieren und sie im empfangenden Paket zum Alias zu machen: *{$import_caller.'::'.$name} = \*{ $name };
  3. In den Pair-Prozessoren mache ich folgendes:

    local *::_ = \$_[0];
    local *k   = \$_[0];
    local *v   = \$_[1];
    @res = $block->( $_[0], $_[1] );

Daher sind $k und $v Aliase dessen, was sich in der Warteschlange befindet. Wenn dies nicht der Fall sein muss, dann könnte es ausreichen, etwas Ähnliches wie folgt zu tun:

local ( $k, $v ) = splice( @_, 0, 2 );
local $_         = $k;

Aber modifizierbare Kopien erlauben es mir auch, Dinge wie folgt zu tun:

each_pair { substr( $k, 0, 1 ) eq '-' and $v++ } %some_hash;

UPDATE:

Es scheint, dass du den Schritt #2 vernachlässigst. Du musst sicherstellen, dass das Symbol im Client-Paket zu deinem Symbol abgebildet wird. Es kann so einfach sein wie:

our $item;

sub import { 
    my $import_caller = caller();
    {   no strict 'refs';
        *{ $import_caller . '::item' } = \*item;
    }
    # Jetzt, ruf Exporter auf!
    goto &{ Exporter->can( 'import' ) };
}

Dann, wenn du dein eigenes Symbol lokalisiert, wird das aliaste Symbol im Client-Paket ebenfalls lokalisiert.

Der Hauptweg, wie ich sehe, dass es ohne das local funktionieren würde, ist, wenn du es aus demselben Paket aufrufst. Andernfalls sind $SomePackage::item und $ClientPackage::item zwei unterschiedliche Dinge.

2voto

flesk Punkte 7274

Ich denke, das ist ein bisschen gehackt, aber Sie könnten so etwas machen:

#!/usr/bin/perl
use strict;
use warnings;

my $item;

sub process_and_store(&@) {
    my $code = shift;
    for (@_) {
        $item = $_;
        &$code();
    }
    undef $item;
}

Das Ding ist, $item muss ein globaler Skalar sein, damit das funktioniert, also muss process_and_store diesen Skalar aktualisieren, während es über die Liste iteriert. Sie sollten außerdem undef $item am Ende der Unterfunktion setzen, um mögliche Seiteneffekte zu begrenzen. Wenn ich so etwas schreiben würde, würde ich es in einem Modul verstecken und es möglich machen, die Iterator-Variable zu definieren, um Namenskonflikte zu begrenzen.

Test:

my @list = qw(Äpfel Birnen Bananen);
process_and_store { do_something_with($item) } @list;

sub do_something_with {
    my $obst = shift;
    print "$obst\n";
}

Ausgabe:

Äpfel
Birnen
Bananen

2voto

LeoNerd Punkte 8068

Die Variablen $a und $b sind in Perl speziell; sie sind echte globale Variablen und daher von use strict ausgenommen und speziell von der Funktion sort() verwendet.

Die meisten anderen ähnlichen Verwendungen in Perl würden die globale Variable $_ für solche Dinge verwenden:

process_and_store { do_something_with( $_ ) } @list;

Was bereits von den normalen Regel des $_ abgedeckt wird. Vergiss nicht, local zu verwenden für $_:

sub process_and_store(&@)
{
  my $code = shift;
  foreach my $item (@_) {
    local $_ = $item;
    $code->();
  }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X