如何在三个值之间“映射”?

时间:2011-03-11 10:11:57

标签: perl data-structures

我有很多像这样的数据

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

数据应该使用什么数据结构?

这些功能怎么样?

更新:所有数据都是唯一的。

6 个答案:

答案 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 documentationuse 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,如下所述,我们构造一个键,其键为

  1. 从名称
  2. 到名称
  3. 前图像
  4. 例如,规范中的第一个数据行(aax,ert​​,ddd)对应于六个(= 3 P 2 )条目:

    1. $map{type1}{type2}{aax} = "ert"
    2. $map{type1}{type3}{aax} = "ddd"
    3. $map{type2}{type1}{ert} = "aax"
    4. $map{type2}{type3}{ert} = "ddd"
    5. $map{type3}{type1}{ddd} = "aax"
    6. $map{type3}{type2}{ddd} = "ert"
    7. 有了哈希,我们然后在调用者的命名空间中安装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