6

headers を使用して多次元テーブルを実装しようとしています。

2D の例を次に示します。

                       < dimension1 >
    /\               'column0'  'column1'
dimension0   'row0'   data00     data10
    \/       'row1'   data01     data11

行と列のヘッダーはテキストで、データは何でも構いません。このようなことができるようになりたいです (構文は異なる場合があります。私は Perl の初心者です)。

my $table = new table(2); # 2 is the number of dimensions

# the following line creates a new row/column if it didn't exist previously
$table['row0']['column0'] = data00;
$table['row0']['column1'] = data01;
$table['row1']['column0'] = data10;
$table['row1']['column1'] = data11;

# the following line returns the headers of the specified dimension
$table->headers(0);
 => ('row0', 'row1')

最初の質問:このようなことは CPAN で既に行われていますか? (あなたが尋ねる前に、私はかなりの時間を検索しましたが、そのようなものは見つかりませんでした)


2 番目の質問:これが私の試みです。見苦しく、おそらく間違っていることはわかっています。Perl の専門家で私のコードをレビューしてくれる人はいますか?

package table;

sub new {
  my $class = shift;
  my $dimensions = shift;
  my $self = bless({}, $class);
  $self->{dimensions} = $dimensions;
  $self->{data} = [];
  $self->{headers} = [];
  return $self;
}

sub get_dimensions {
  my $self = shift;
  return $self->{dimensions};
}

# This function creates a header or return its index if it already existed.
# Headers are encoded as an array of hashes so that this is O(1) amortized.

sub header {
  my $self = shift;
  my $dimension = shift;
  my $header = shift;
  my $headers = $self->{headers}[$dimension];
  if(!defined($headers)) {
    $headers = $self->{headers}[$dimension] = {};
  }
  if(!defined($headers->{$header})) {
    $headers->{$header} = scalar keys %$headers;
  }
  return $headers->{$header};
}

# This function returns the list of headers. Because the headers are
# stored as a hash (`header=>index`), I need to retrieve the keys
# and sort them by value.

sub get_headers {
  my $self = shift;
  my $dimension = shift;
  my $headers = $self->{headers}[$dimension];
  return [sort { $headers->{$a} cmp $headers->{$b} } keys %$headers];
}

# This last function stores/retrieves data from the table.

sub data {
  my $self = shift;
  my $data = $self->{data};
  my $dimensions = $self->{dimensions};
  for(my $i = 0; $i < $dimensions-1; ++$i) {
    my $index = $self->header($i, shift);
    if(!defined($data->[$index])) {
      $data->[$index] = [];
    }
    $data = $data->[$index];
  }
  my $index = $self->header($dimensions-1, shift);
  my $value = shift;
  if(defined($value)) {
    $data->[$index] = $value;
  }
  return $data->[$index];
}
4

3 に答える 3

2

「N」次元テーブルの構造が必要です。それほど一般的な状況ではないため、これを実行できる CPAN モジュールがあるとは思えません。

問題は、データ構造が非常に急速に拡大し、複雑さも増していることです。

N 次元のテーブルを 1 つのリストに格納するには、ちょっとした数学を使用して N 次元の配列を 1 つの次元に変換します。X が X 次元を表し、X' がその次元の長さを表すとします。2 次元のテーブルの場合、次のようにして値を取得できます。

X * Y` + Y.

3 次元のテーブル X、Y、Z の場合、答えは次のようになります。

X * (Y' * Z') + Y * Z' + Z

4 次元のテーブル W、X、Y、Z の場合、答えは次のようになります。

W * (X' * Y' * Z') + X * (Y' + Z') + Y * Z' + Z'

(数学が正しいことを願っています)。

したがって、N次元のテーブルの場合、このような構造を想像できます。これには 2 つの異なるクラスが含まれます。1 つは次元情報を表し、もう 1 つは実際のデータ (すべての次元を含む) を表します。

  • ディメンション (クラス)
    • 見出し (英数字文字列)
    • 次元のサイズ (整数)
  • N テーブル (クラス)
    • 次元の配列 (次元クラス オブジェクト)
    • データの配列 (英数字文字列)

次元数は、次を参照して取得できます。

my $numOfDimensions = scalar @{$ntable->{DIMENSIONS}};

そして、以下を見ることで次元の見出しを得ることができます$x:

my xDimensionHeading = $ntable->{DIMENSION}->[$x]->{HEADING};

そして、以下を見て、その次元のサイズ:

my xDimensionSize = $ntable->{DIMENSION}->[$x]->{SIZE};

もちろん、裸の参照ではなく、真のオブジェクト指向呼び出しでこれを行いますが、これにより、構造がどのように機能するかがわかります。

ここで、セルの位置を表す整数のリストを 1 次元配列に沿ったセルの位置に変換する方法が必要になり、データを取得および取得する方法が得られます。

これはあなたが探しているものでしょうか?


編集

それに近いですが、実際にはテーブルのサイズを大幅に変更します(事前にサイズを決定することはできません)。あなたのソリューションがこれに対応していないことがわかった場合。

これは多くの複雑さを追加します...

Dimension クラスで Size を捨てる必要があります。また、データを格納するために 1 次元配列を使用することはできません。

テーブルの次元を変更しないでください。

次のようなことができます。

  • N テーブル (クラス)
    • 次元見出しのリスト {DIMENSION}->[]
    • List to Data {DATA}->[] (これは他のリストへのリンクである可能性があります)

{DATA} リストは、テーブルの深さに応じたリストのリンクです。例えば:

 my data_3D = $table_3D->{DATA}->[$x]->[$y]->[$z];
 my data_2D = $table_2D->{DATA}->[$x]->[$y];

次元数は ですscalar @{$table->{DIMENSION}}

問題は、次元的に中立な方法でデータにアクセスする方法です。2、3、4、またはそれ以上の次元が必要になる可能性があり、それを引き出すには何らかの方法で住所を構造化する必要があります。

ある種のループ メカニズムを持つことができます。で座標のリストを取得し、@coordinates各座標を調べます。最後はデータを指します。残りは、単に別の配列への別の参照になります。

 my $data = pop @coordinates;    #First Coordinate
 $data = $table->[$data];        #Could be data if 1D table, could be a reference
 foreach my $coordinate (@coordinates) {
    die qq(Not enough coordinates) if ref $data ne 'ARRAY';
    $data = $data->[$coordinate];   #Could be data, could be a reference
 }

 # Cell value is in $data

座標のリストを作成し、それを評価することも可能です。ここでも完全にテストされていません:

 $coordinates = "[" . join ("]->[" => @coordinates . "]";

座標が 3 つある場合は、次のようになります。

 $coordinates = "[$x]->[$y]->[$z]";

1次元配列がどのように機能するかわかりません...

そこから、ステートメントを作成して使用evalし、データを取得できます。

いくつかの方法を用意する必要があります。

  • 寸法を設定する
  • セルを設定する
  • セルを取得する
  • テーブルが完全であることを確認します (これがどのように機能するかわかりません。

これはブレインダンプですが、これでうまくいくと思います。テーブルの次元が設定されておらず、任意の N 次元のテーブルで機能する可能性があります。

于 2011-09-27T18:21:48.180 に答える
1

Text::TabularDisplayを使用してこれを行うことができる場合があります。これは、あなたの例で行った簡単な試行です。

use strict;
use warnings;
use Text::TabularDisplay;

my $t = Text::TabularDisplay->new(('', 'column0', 'column1'));
$t->add('row0', 'data00', 'data10');
$t->add('row1', 'data01', 'data11');
print $t->render;

ショー:

+------+---------+---------+
|      | column0 | column1 |
+------+---------+---------+
| row0 | data00  | data10  |
| row1 | data01  | data11  |
+------+---------+---------+

これがまさにあなたが探していたものかどうかはわかりません。最初の列を空白のままにして、ヘッダーをごまかす必要があります。

于 2011-09-27T14:35:18.457 に答える
0

Text::Tableはここで役立つかもしれません。以下に簡単な例を示します。モジュールが提供するさまざまなオプションを試して、記述に近いものを作成できます。

#!/usr/bin/perl

use warnings;
use strict;

use Text::Table;

my $inner_table = Text::Table->new(qw(column0 column1));

$inner_table->load(
    [ qw(row0 data00 data01) ],
    [ qw(row1 data10 data11) ],
);

my $outer_table = Text::Table->new(' ', 'dimension1');

$outer_table->load(
    ['dimension0', $inner_table->stringify ],
);

print $outer_table;

出力

C:\Temp> t
           次元1
次元0 列0 列1
           行0 データ00
           行1 データ10
于 2011-09-27T15:07:19.613 に答える