我有很多像这样的数据
type1, type2, type3
aax, ert, ddd
asx, eer, kkk
xkk, fff, lll
xxj, vtt, lle
...
我真的希望能够在它们之间“映射”,所以我可以从
开始type1 -> type2
type1 -> type3
type2 -> type1
type3 -> type1
示例:
type1_to_type2(aax) should return ert
type1_to_type3(asx) should return kkk
type2_to_type3(fff) should return lll
type3_to_type1(lle) should return xxj
数据应该使用什么数据结构?
这些功能怎么样?
更新:所有数据都是唯一的。
答案 0 :(得分:3)
如果所有字符串都是唯一的,您可以将它们用作哈希中的键:
my %data = (
aax => ["aax", "ert", "ddd"],
ert => ["aax", "ert", "ddd"],
ddd => ["aax", "ert", "ddd"],
asx => ["asx", "eer", "kkk"],
...
);
sub get_value {
my ($s, $type) = @_;
return $data{$s}[$type-1];
}
print get_value("aax", 2); # "ert"
答案 1 :(得分:3)
一种方法是将数据库用于此类事情。这是一个例子:
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect("dbi:SQLite:dbname=demo_db","","");
# Initialize an SQLite DB with some content.
my @init_db = (
'CREATE TABLE demo (ty1 VARCHAR(5), ty2 VARCHAR(5), ty3 VARCHAR(5));',
'INSERT INTO demo (ty1, ty2, ty3) values ("aax", "ert", "ddd");',
'INSERT INTO demo (ty1, ty2, ty3) values ("asx", "eer", "kkk");',
'INSERT INTO demo (ty1, ty2, ty3) values ("xkk", "fff", "lll");',
'INSERT INTO demo (ty1, ty2, ty3) values ("xxj", "vtt", "lle");',
);
for my $s (@init_db){
$dbh->do($s) or die $!;
}
# Query the data by any field we like.
my $sth = $dbh->prepare('SELECT * FROM demo');
$sth->execute();
my $result = $sth->fetchall_hashref('ty1');
结果是对哈希散列的引用,由ty1
的值键入,然后由我们表中的字段名称键入。例如:
$result->{xkk} = {
'ty2' => 'fff',
'ty3' => 'lll',
'ty1' => 'xkk'
};
如果您只对给定字段的一个特定值感兴趣,则可以使用更具体的查询。使用这种方法,编写实用程序方法非常容易 - 例如,获取字段名称和感兴趣的值,并以最方便的格式返回结果。
答案 2 :(得分:3)
实际实现'type1_to_type2'等功能的版本。
#!/usr/bin/perl
use strict;
use warnings;
my $data;
while (<DATA>) {
chomp;
push @$data, [ split ];
}
sub typeX_to_typeY {
my ($x, $y, $str) = @_;
foreach (@$data) {
if ($_->[$x - 1] eq $str) {
return $_->[$y - 1];
}
}
return;
}
sub type1_to_type2 { typeX_to_typeY(1, 2, @_) }
sub type1_to_type3 { typeX_to_typeY(1, 3, @_) }
sub type2_to_type1 { typeX_to_typeY(2, 1, @_) }
sub type2_to_type3 { typeX_to_typeY(2, 3, @_) }
sub type3_to_type1 { typeX_to_typeY(3, 1, @_) }
sub type3_to_type2 { typeX_to_typeY(3, 2, @_) }
# tests
use Test::More tests => 4;
is(type1_to_type2('aax'), 'ert');
is(type1_to_type3('asx'), 'kkk');
is(type2_to_type3('fff'), 'lll');
is(type3_to_type1('lle'), 'xxj');
__DATA__
aax ert ddd
asx eer kkk
xkk fff lll
xxj vtt lle
答案 3 :(得分:2)
使用一个数组,其中包含:
(
['aax', 'ert', 'ddd'],
['asx', 'eer', 'kkk'],
...,
)
和三个hashref的数组,第一个hashref包含:
{
aax => $array[0],
asx => $array[1],
...,
}
第二个hashref包含:
{
ert => $array[0],
eer => $array[1],
...,
}
等(所有三个哈希指向同一原始数组中的条目)。然后找到对应于“column 1 ='asx'”的“第三列”值,查找只是$table[0]{asx}[2]
,这将给出“kkk”。
答案 4 :(得分:2)
如果所有字符串都是全局唯一的,那么'eugene'解决方案将正常工作。
如果字符串不是全局唯一的,那么它们至少需要在位置上唯一才能使您的问题有意义(除非当然允许多个答案);即值应按列唯一。
如果是这种情况,那么你可以使用eugene-solution但是将列号附加到散列键,如下所示:
my %data = (
aax1 => ["aax", "ert", "ddd"],
ert2 => ["aax", "ert", "ddd"],
ddd3 => ["aax", "ert", "ddd"],
asx1 => ["asx", "eer", "kkk"],
...
);
sub get_value {
my ($string, $from_type, $to_type) = @_;
return $data{$string . $from_type }[ $to_type - 1 ];
}
print get_value("aax", 1, 2); # "ert"
答案 5 :(得分:2)
让我们首先想象一下我们想要存在的世界。
#! /usr/bin/env perl
use strict;
use warnings;
use Convert q(
type1, type2, type3
aax, ert, ddd
asx, eer, kkk
xkk, fff, lll
xxj, vtt, lle
);
有了这个前沿问题,我们应该能够调用一些方便的功能:
use Test::More;
diag type1_to_type2("aax");
diag type1_to_type3("asx");
diag type2_to_type3("fff");
diag type3_to_type1("lle");
结果应与表格中的内容相对应。
my @tests = (
[ qw/ type1_to_type2 aax ert / ],
[ qw/ type1_to_type3 asx kkk / ],
[ qw/ type2_to_type3 fff lll / ],
[ qw/ type3_to_type1 lle xxj / ],
[ qw/ type2_to_type1 ert aax / ],
);
my %sub_ok;
for (@tests) {
my($name,$from,$expect) = @$_;
my $sub;
{ no strict 'refs';
unless ($sub_ok{$name}++) {
ok defined &$name, "$name defined"
or next;
}
$sub = \&$name;
}
is $sub->($from), $expect, "$name($from)";
}
done_testing;
为了实现这一目标,转换模块需要采用规范并生成适当的子。
Convert.pm
中的代码以熟悉的样板开头。
package Convert;
use strict;
use warnings;
According to the perlfunc documentation,use Module LIST
相当于
BEGIN { require Module; Module->import( LIST ); }
所以Convert的import
需要将表作为其参数之一。 (我们忽略的第一个是字符串"Convert"
,因为import
被称为类方法。)
sub import {
my(undef,$spec) = @_;
my %map;
my @names;
_populate(\%map, \@names, $spec);
my $pkg = caller;
foreach my $n1 (@names) {
foreach my $n2 (@names) {
next if $n1 eq $n2;
my $sub = sub {
my($preimage) = @_;
return unless exists $map{$n1}{$n2}{$preimage};
$map{$n1}{$n2}{$preimage};
};
my $name = $pkg . "::" . $n1 . "_to_" . $n2;
{ no strict 'refs'; *$name = $sub; }
}
}
}
使用_populate
,如下所述,我们构造一个键,其键为
例如,规范中的第一个数据行(aax,ert,ddd)对应于六个(= 3 P 2 )条目:
$map{type1}{type2}{aax} = "ert"
$map{type1}{type3}{aax} = "ddd"
$map{type2}{type1}{ert} = "aax"
$map{type2}{type3}{ert} = "ddd"
$map{type3}{type1}{ddd} = "aax"
$map{type3}{type2}{ddd} = "ert"
有了哈希,我们然后在调用者的命名空间中安装subs( eg ,type1_to_type2
),每个子命令在相应的槽中查找其参数并返回映射的image如果它存在。
在_populate
中,我们从第一个非空行中获取列名。对于剩余的数据行,每对值都会进入映射。
sub _populate {
my($map,$names,$spec) = @_;
my $line;
for (split /\s*\n\s*/, $spec) {
++$line;
my @fields = split /\s*,\s*/;
next unless @fields;
if (@$names) {
my %f;
@f{@$names} = @fields;
unless (@fields == @$names) {
warn "$0: line $line: number of fields and columns do not match!\n";
next;
}
foreach my $n1 (@$names) {
foreach my $n2 (@$names) {
next if $n1 eq $n2;
my($f1,$f2) = @f{$n1,$n2};
my $slot = \$map->{$n1}{$n2}{$f1};
warn "$0: line $line: discarding $$slot ($n1 -> $n2)\n"
if defined $$slot;
$$slot = $f2;
}
}
}
else {
@$names = @fields;
}
}
}
不要忘记让模块最后返回一个真值。
1;
最后,输出!
# ert # kkk # lll # xxj ok 1 - type1_to_type2 defined ok 2 - type1_to_type2(aax) ok 3 - type1_to_type3 defined ok 4 - type1_to_type3(asx) ok 5 - type2_to_type3 defined ok 6 - type2_to_type3(fff) ok 7 - type3_to_type1 defined ok 8 - type3_to_type1(lle) ok 9 - type2_to_type1 defined ok 10 - type2_to_type1(ert) 1..10