我正在尝试使用 header 实现一个多维表。

这是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中是否已经做过类似的事情? (在您问我确实搜索了大量时间之前,我没有找到类似的东西)

第二个问题:这是我的尝试,我知道它很丑,而且可能是错误的。有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];
}

最佳答案

您需要一个“N”维表的结构。我怀疑是否有一个CPAN模块可以做到这一点,因为这并不常见。
问题在于数据结构增长非常迅速,复杂性也是如此。
您可以通过使用一些数学运算将N维表转换为单个维,从而将N维表存储在单个列表中。假设X代表X维度,X'代表该维度的长度。对于二维表,您可以通过执行以下操作获取值:

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维表的结构是这样的。它涉及两个不同的类:一类代表维信息,另一类代表实际数据(包括所有维)。
  • Dimension(类)
  • 标题(字母数字字符串)
  • 尺寸大小(整数)

  • N表(类)
  • 维数组(维类对象)
  • 数据数组(字母数字字符串)


  • 您可以通过查看以下内容获取尺寸数:
    my $numOfDimensions = scalar @{$ntable->{DIMENSIONS}};
    
    并且,您可以通过查看以下内容获得维度$x的标题:
    my xDimensionHeading = $ntable->{DIMENSION}->[$x]->{HEADING};
    
    并且,通过查看以下内容来确定该维度的大小:
    my xDimensionSize = $ntable->{DIMENSION}->[$x]->{SIZE};
    
    当然,您将使用真正的面向对象的调用(而不是裸引用)来执行此操作,但这可以使您了解结构的工作方式。
    现在,您需要一种将表示单元格位置的整数列表转换为沿一维数组的单元格位置的方法,并且您将有一种获取和检索数据的方法。
    这是您要找的东西吗?

    编辑

    这增加了很多复杂性...
    我们需要在Dimension类中扔掉Size。而且,我们不能使用一维数组来存储我们的数据。
    希望您不要更改表的维数。
    我们可以做这样的事情:
  • N表(类)
  • 维度标题列表{DIMENSION}-> []
  • 到数据{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 . "]";
    
    如果有三个坐标,这将是
     $coordinates = "[$x]->[$y]->[$z]";
    
    我不确定一维数组将如何工作...
    从那里,您可以构建一条语句并在其上使用eval并获取数据。
    您将必须有几种方法。
  • 设置尺寸
  • 设置单元格
  • 检索单元格
  • 验证表是否完整(我不知道这将如何工作。

  • 这更像是一个脑力激荡,但我认为这可能行得通。您没有任何设置的表尺寸,并且可能适用于任何N维表。

    09-19 06:56