2012-05-02 47 views
2

在Perl中,當使用sort函數進行自定義比較時,已將變量$a$b分配給當前要比較的元素對,例如,在:如何調用預先分配給某個值的變量的子程序?

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

如何編寫其他子程序具有類似的功能?例如,假設我想寫一種process_and_store函數,它對列表中的每個項目做一些特殊的處理,然後將其存儲在數據庫中;並且變量$item已被分配給正在處理的當前項目。我想編寫例如類似:

process_and_store { do_something_with($item); } @list; 

不是

process_and_store { my $item = shift; do_something_with($item); } @list; 

我應該如何去這樣做呢?


UPDATE:爲了完整起見,雖然flesk's answer作品沒有問題,爲了「正確」定位的變化讓我對$item可變我都得跟着advice from Axeman。在SomePackage.pm我把這樣的:

#!/usr/bin/perl -w 

use strict; 
use SomePackage; 

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

,並得到預期的結果:

seen: 1 
stored: 1 
seen: 2 
stored: 2 
seen: 3 
stored: 3 

回答

4

在我的 「關聯數組」 處理庫,我做同樣的事情。用戶可以導出變量$k$v(鍵值),使他們能夠做這樣的事情:

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

這裏是我如何做到這一點:

  1. 我在執行包申報our ($k, $v)
  2. import我允許包以導出在 那些符號和別名它們接收包裝:*{$import_caller.'::'.$name} = \*{ $name };
  3. 在一對處理器,I執行以下操作:

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

因此$k$v排隊內容的別名。如果這並不一定是這種情況,那麼你可能會與一些足夠的快樂如下:

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

但修改副本也讓我做這樣的事情:

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

UPDATE:

看來你忽略第2步。您必須確保客戶端軟件包中的符號映射到您的符號。它可以是簡單:

our $item; 

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

然後,當你本地化您自己的符號,在客戶端軟件包的別名符號本地化爲好。

我可以看到它的工作方式沒有local的主要方式是,如果你是從同一個包中調用它的話。否則,$SomePackage::item$ClientPackage::item是兩個截然不同的東西。

+0

我試圖按照你的建議來「本地化」變量名的變化;但不能讓它工作。我已經用細節更新了我的問題。你能幫忙嗎? –

+0

@JuanAntonio,查看更新。 – Axeman

+0

嗯..我期待'我們的@EXPORT = qw(process_and_store $ item);'照顧這個。事實上,如果我按照你的建議添加一個'import' sub(並從導出列表中刪除'$ item'),'process_and_store'似乎不再被導出。 –

2

我認爲這是一個有點

package SomePackage; 

use strict; 

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

our $item; 

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

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

1; 

然後,我喜歡的東西稱之爲從main.pl黑客,但你可以做這樣的事情:

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

my $item; 

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

事情是,$item必須是全局標量才能工作,因此process_and_store必須在列表中循環時更新該標量。您還應該在子程序結束時使用undef $item以限制任何潛在的副作用。如果我要寫這樣的東西,我會把它放在模塊中,並且可以定義迭代器變量,以限制名稱衝突。

測試:

my @list = qw(apples pears bananas); 
process_and_store { do_something_with($item) } @list; 

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

輸出:

apples 
pears 
bananas 
+0

一個詞法變量('my')將不會在呼叫範圍內是可見的,除非它是在同一個文件或範圍。它應該是'我們',它應該是*'本地化'*。 – Axeman

+0

@Axeman:是的,我的回答是在更新之前寫的,現在你已經寫了這樣一個徹底的答案,我沒有理由更新我的。 – flesk

2

$a$b變量在Perl中是特殊的;它們是真正的全局變量,因此可以免除use strict,並且還專門用於sort()函數。在Perl

大多數其他類似用途將使用$_全球市場對這樣的事情:

process_and_store { do_something_with($_) } @list; 

這已經是由正常$_規則進行處理。不要忘了local ISE $_

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

感謝您的回答,我試着按照您的建議,但是當我將代碼放入模塊內時看起來不太可行(請參閱我的更新問題)。你能幫忙嗎? –