2009-12-12 49 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?