使用相同散列的元素声明散列

Declaring a hash using elements of the same hash

愚蠢的 perl 问题。假设我正在准备一些 API 调用,为了整洁起见,我想在散列中声明所需的变量。我如何引用我正在声明的相同散列?

my %api_hash = (
     'api_endpoint1' => {
             'username' => 'foo',
             'password' => 'bar',
             'api_url'  => 'https://192.168.1.10:9200',
             'headers'  => {Accept => 'application/json', Authorization => 'Basic ' . encode_base64($username . ':' . $password)}
     },
     'api_endpoint2' => {
             'username' => 'bah',
             'password' => 'wizz',
             'api_url'  => 'https://192.168.1.20:9182',
             'headers'  => {Accept => 'application/json', Authorization => 'Basic ' . encode_base64($username . ':' . $password)}
     }
);

而不是 $api_hash{<endpoint name>}{'headers'} 中的 $username$password,我如何在同一声明语句中将它们设置为 $api_hash{<endpoint name>}{'username'}$api_hash{<endpoint name>}{'password'}

您可以在散列中声明除 headers 之外的所有内容,然后在声明后使用 for 循环添加它们:

use warnings;
use strict;

my %api_hash = (
     'api_endpoint1' => {
             'username' => 'foo',
             'password' => 'bar',
             'api_url'  => 'https://192.168.1.10:9200',
     },
     'api_endpoint2' => {
             'username' => 'bah',
             'password' => 'wizz',
             'api_url'  => 'https://192.168.1.20:9182',
     }
);

for my $ep (keys %api_hash) {
    my $auth = 'Basic ' . encode_base64($api_hash{$ep}{username} . ':' . $api_hash{$ep}{password});
    $api_hash{$ep}{headers} = {Accept => 'application/json', Authorization => $auth}
}

您可以使用一个函数从您的三个输入构建四个键值对。

sub api_endpoint {
    my ($user,$pw,$url) = @_;
    return { username => $user, password => $pw, api_url => $url,
             headers => { Accept => 'application/json', 
                          Authorization => 'Basic ' . encode_base64($user . ':' . $pw)} }
}

my %api_hash = (
    api_endpoint1 => api_endpoint('foo','bar','https://192.168.1.10:9200'),
    api_endpoint2 => api_endpoint('bah','wizz','https://192.168.1.20:9182'),
    ...
);

或者为了保留散列声明的更多外观和感觉,让函数接受尽可能多的散列引用和return带有第四个参数的散列引用

sub api_endpoint {
    my ($ep) = @_;
    $ep->{headers} = {
        Accept => 'application/json', 
        Authorization => 'Basic ' 
             . encode_base64($ep->{username} . ':' . $ep->{password}
    };
    return $ep;
}

my %api_hash = (
     'api_endpoint1' => api_endpoint({
             'username' => 'foo',
             'password' => 'bar',
             'api_url'  => 'https://192.168.1.10:9200',
     }),
     'api_endpoint2' => api_endpoint({
             'username' => 'bah',
             'password' => 'wizz',
             'api_url'  => 'https://192.168.1.20:9182',
     })
);

对于一次性申请,你所做的就很好了。如果您要经历使某些代码可重用的麻烦,最好花更少的时间,而不是编写额外的“蓬松”代码。如果您在哈希中手动写入用户名和密码,也只需在第二个位置手动写入即可。

如果没有关于您正在做的其他事情的更多信息,很难说什么是做您想做的事的正确方法[TM]。您可以使用 mob 显示的生成器。 “最正确”的方法可能是使它成为一个对象并使headers键成为一个方法调用。

{ package MyEndpoint;
  sub new {
    my $class = shift;
    my $self = { @_ };
    return bless $self, $class;
  }
  sub get_headers {
    my self = shift;
    my %headers = %{$self->{-headers}};
    if ($self->{-auth} eq "basic") {
      $headers{Authorization} = sprintf "Basic %s", 
        encode_base64(join ":", @{$self}{qw/-username -password/});
    }
    return %headers;
}

my %api_hash = (
     api_endpoint1 => MyEndpoint->new(
             -username => 'foo',
             -password => 'bar',
             -api_url  => 'https://192.168.1.10:9200',
             -auth     => 'basic',
             -headers  => {Accept => 'application/json'},
     },
     ...
);

## Later on when using the endpoint
foreach my $endpoint (values %api_hash) {
  my @headers = $endpoint->get_headers;
  ...
}

但是要直接回答您的问题,要在定义数据结构时使用数据结构中的值,您必须滥用解析器。

my %api_hash;
for (1, 2) {
  %api_hash = (
     'api_endpoint1' => {
         'username' => 'foo',
         'password' => 'bar',
         'api_url'  => 'https://192.168.1.10:9200',
         'headers'  => {Accept => 'application/json', 
           Authorization => 'Basic ' . encode_base64(
           $api_hash{api_endpoint1}{username} . ':' . $api_hash{api_endpoint1}{password}
           )}
     },
     'api_endpoint2' => {
         'username' => 'bah',
         'password' => 'wizz',
          'api_url'  => 'https://192.168.1.20:9182',
          'headers'  => {Accept => 'application/json', 
            Authorization => 'Basic ' . encode_base64(
            $api_hash{api_endpoint2}{username} . ':' . $api_hash{api_endpoint2}{password}
            )}
     }
  );

}

没错,你用循环执行了两次赋值。第一遍设置所有普通值;此时所有的自引用都是undef。在第二遍中,所有普通键和值现在都存在,因此自引用将解析为实际值。你明白为什么它是解析器滥用。您正在定义和覆盖整个事物两次以获得这些自我引用。对于一些又小又快又脏的东西来说很好。这是 Perl。然而,它对于 Real Code[TM] 来说也是浪费和蹩脚的。

最好从离散变量构建它或使它成为一个对象。这是对象很棒的主要原因之一。您可以将所有样板文件放入一个模块中,并仅在您真正需要它们时才生成它们。

HTH