2009-12-12 84 views
3

我正在使用類似於以下的哈希表來存儲可以在提示時輸入的字母以及該選項的描述和將被調用的函數。將值綁定到Perl中的函數

my %main_menu = (
    "l" => ["list locks", \&list_locks], 
    "n" => ["new lock", \&new_lock], 
    "u" => ["update lock", \&update_lock] 
    ); 

menu_prompt(\%main_menu)產生以下菜單:

________________________ 
| l - list locks   | 
| n - new lock   | 
| u - update lock  | 
|________________________| 
(l,n,u)>

當用戶輸入 'U',在提示中,update_lock功能將被調用。

現在,我想用一個新的散列表(%lock_menu)產生一個類似的菜單。但是,我將首先提示用戶輸入他們希望更新的鎖的ID。

Please enter the ID of a lock to update: 1 

You are updating lock 1. 
__________________ 
| l - list users | 
| n - new user  | 
|__________________| 
(p,u,c)>

我想存儲鎖ID,以便它可以通過鎖菜單功能訪問。例如:

my %users_menu = (
"l" => ["list users", \&list_users], 
"n" => ["new user", \&new_user]);

我無法弄清楚如何「附加」鎖ID的功能,在%users_menu。所以當選擇'l'時,list_users將被作爲第一個參數被調用。

我似乎記得ML如果你只用一個參數調用ML語言中的n參數函數,它將產生一個函數,它需要n-1個參數。例如,調用func(int,int,int)作爲func(5)會產生func(int,int),並將第一個參數保存爲5.

這在Perl中可能嗎?或者我正在以這種錯誤的方式去做?請告訴我。

UPDATE:這是打印一份菜單(print_options)功能,提示用戶輸入一個字母,並調用相應的功能。

sub menu_prompt 
{ 
    my $options = shift; 

    print_options $options; 

    my $choice = <>; 
    chomp $choice; 

    if (defined $$options{$choice}) 
    { 
     $$options{$choice}[1](); # no arguments 
    } 
}

我想找到一種方法,使用該功能適用​​於所有的菜單,而不是寫一個值的傳遞給函數一個單獨的函數。

回答

6

沒有發佈更多的示例代碼它很難給出完整的答案,但是當你從哈希中調用你的子,爲什麼不傳遞它的鎖定值?

my $num = ... # get lock number; 

$users_menu{"n"}[1]->($num) 

# calls "new_user" passing it $num 

問題編輯:

sub menu_prompt { 
    my $options = shift; 

    print_options $options; 

    my $choice = <>; # i assume the diamond operator got stripped 
    chomp $choice; # second my is in error 

    if (defined $$options{$choice}) { 
     return $$options{$choice}[1](@_); 
      # any additional arguments to menu_prompt will be passed to the sub 
      # return the value for future processing 
    } 
} 
+0

這就是我一直在尋找,謝謝! – titaniumdecoy 2009-12-12 03:07:09

+0

和我自己的代碼中,我可能會寫最後一行爲'goto&{$$ options {$ choice} [1]};'這是相當和更快的,但對於那些學習Perl的人來說不太清楚Perl – 2009-12-12 04:26:44

+0

Eric:爲了那些仍然在學習的人,至少要在臨近之前添加一些臨時工。除了。這不是關於表現。 – tsee 2009-12-12 14:33:54

1

我不得不說,我不完全理解你的問題,但匿名子可以幫助你

my $id = (somehow get the ID); 

my %users_menu = (
    "l" => ["list users", sub {list_users($id)}], 
        #now, the id is fixed and the subroutine can be called without arguments 
    "n" => ["new user", \&new_user]); 
+0

$ id變量來自哪裏? (我希望在函數被調用時提供它。)另外,在程序運行之前不會評估轉換嗎? – titaniumdecoy 2009-12-12 02:40:17

+0

這個轉變是錯誤的,所以我刪除了它。 $ id變量必須在這個散列值被定義之前被定義。然後,可以不帶參數地調用匿名函數。我會編輯它,所以它更清晰 – 2009-12-12 02:45:02

+0

謝謝,但我需要在運行時根據用戶輸入確定$ id變量。 – titaniumdecoy 2009-12-12 02:48:50

6

你是想討好的功能。

有許多CPAN模塊(見帖子末尾)用於柯里化。以下是一個關閉咖喱的例子。

sub curry { 
    my $f = shift; 
    my $a = shift; 
    sub { $f->($a, @_); } 
} 


my ($input, $func); 
$input = 2; 
$func = curry(sub { print join "\n", @_ }, $input); 

$input = 12; 
$func = curry($func , $input); 

$input = 99; 

$func->(4,6,8,10,19); 


#outputs 
2 
12 
4 
6 
8 
10 
19 

另見Data::UtilSub::CurriedSub::Curry

HTH

+0

這是完全正確的,但對許多編碼人員來說可能有點過於抽象(如果功能強大)。我將添加一個直接使用閉包的答案,並解釋更多的用途和屬性。 – 2012-09-04 15:12:56

0

您可以使用閉包。閉包基本上是一個可以「看到」一些外部變量的函數。對於你的榜樣,你會使用這樣的:

sub make_list_users { 
    my ($lock_id) = @_; 
    return sub { 
    <body of list_users> 
    <do something with $lock_id here> 
    } 
} 

該函數返回一個匿名函數「關閉了」 $lock_id,故名「關閉」。和隨後而不是寫

my %users_menu = (
"l" => ["list users", \&list_users], 
"n" => ["new user", \&new_user]); 

你寫

my %users_menu = (
"l" => ["list users", make_list_users($id)], 
"n" => ["new user", make_new_user($id)]); 

注意沒有\&,我們想要的代碼執行這裏。您的代碼還叫

$$options{$choice}[1](); # no arguments 

make_list_users的結果是一個子程序的參考,像以前一樣,除了它具有鎖ID爲「內幕消息」。並且,不是,$lock_id不是全局變量,它是一個簡單的局部變量make_list_users,並且將在每次運行make_list_users時重新設置。唯一的竅門是返回的子例程會記住它的值。

閉包實際上甚至比這更強大:子程序還可以更改(分配)它關閉的任何變量,而不會影響其他實例使用的閉合變量(即,可以從make_list_users返回兩個不同的子例程在同一時間)。

另外,關閉同一個變量的兩個或多個閉包都會看到這個變量的同一個實例,並且可以使用這個「祕密通道」將消息傳遞給對方!

另請參見What's a closure?