带标题的 Perl 多维表
我正在尝试实现一个带标题的多维表格。
下面是 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];
}
I'm trying to implement a multi-dimensional table with headers.
Here's an example for 2D:
< dimension1 >
/\ 'column0' 'column1'
dimension0 'row0' data00 data10
\/ 'row1' data01 data11
The headers for rows and columns are text, and the data is anything. I want to be able to do something like this (syntax can be different, I'm beginner in 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')
First question: Is there something like this already done in CPAN? (before you ask I did search for a significant amount of time and I didn't find anything like it)
Second question: Here's my try, I know it's ugly and probably wrong. Any Perl expert out there care to review my code?
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];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您需要一个“N”维表的结构。我怀疑是否有 CPAN 模块可以做到这一点,因为这种情况并不常见。
问题是数据结构增长得非常快,复杂性也随之增加。
您可以通过使用一些数学方法将 N 维数组转换为单维,将 N 维表存储在单个列表中。假设 X 代表 X 维度,X' 代表该维度的长度。对于二维表,您可以通过以下方式获取值:
对于 3 维表 X, Y, Z,答案为:
对于 4 维表 W, X, Y, Z,答案为:
(I希望数学是正确的)。
因此,我可以想象一个N维表的这样的结构。它将涉及两个不同的类:一个表示维度信息,另一个表示实际数据(包括所有维度)。
您可以通过查看以下方式获取维度数:
并且,您可以通过查看以下方式获取维度
$x
的标题:并且,通过查看以下内容来确定该维度的大小:
当然,您可以使用真正的面向对象调用而不是裸引用来执行此操作,但这可以让您了解该结构如何工作。
现在,您需要一种方法将表示单元格位置的整数列表转换为沿一维数组的单元格位置,并且您将有一种获取和检索数据的方法。
这就是您要找的吗?
编辑
这增加了很多复杂性......
我们需要扔掉 Dimension 类中的 Size。而且,我们不能使用一维数组来存储数据。
我希望你不要改变表的维度。
我们可以这样做:
{DATA} 列表是取决于表深度的列表链接。例如:
维度数为标量@{$table->{DIMENSION}}。
问题是如何以维度中立的方式访问数据。我可能需要 2、3、4 或更多维度,并且我必须以某种方式构建我的地址才能将其拉出来。
我们可以有某种循环机制。我们在
@coordinates
中获取坐标列表,然后查看每个坐标。最后一个将指向数据。其余的只是对另一个数组的另一个引用。也可以建立一个坐标列表,然后对其进行评估。再次完全未经测试:
如果有三个坐标,这将是
我不确定一维数组将如何工作...
从那里,您可以构建一个语句并在其上使用
eval
并得到数据。你必须有几种方法。
这更像是一个脑转储,但我认为这可能有用。您没有任何设置的表格尺寸,它可能适用于任何 N 维表。
You want a structure for an "N" dimensional table. I doubt there's a CPAN module that can do this because it's just not that common a situation.
The problem is that the data structure grows quite rapidly and so does the complexity.
You can store an N dimensional table in a single list by using a bit of mathematics to transform the N dimensional array into a single dimension. Let's say that X represents the X dimension and X' represents the length of that dimension. For a two dimensional table, you could get the value by doing:
For a 3 dimensional table X, Y, Z, the answer would be:
For a 4 dimensional table W, X, Y, Z, the answer would be:
(I hope the math is right).
Therefore, I can imagine a structure like this for an N dimensional table. It would involve two different classes: One represents the dimensional information and the other represents the actual data (including all of the dimensions).
You can get the number of dimensions by looking at:
And, you can get the heading of dimension
$x
by looking at:And, the size of that dimension by looking at:
Of course, you'd do this with true object oriented calls, and not bare references, but this gives you an idea how the structure would work.
Now, you need a way of transforming a list of integers that would represent a cell's location into a cell's location along a single dimensional array, and you'll have a way of getting and retrieving your data.
Would this be what you're looking for?
EDIT
This adds a lot of complication...
We need to throw out the Size in the Dimension class. And, we can't use a single dimensional array to store our data.
I hope you don't change the table dimensionality.
We could do something like this:
The {DATA} list is a link of lists depending on the depth of the table. For example:
The number of dimensions is
scalar @{$table->{DIMENSION}}
.The question is how do I access the data in a way that's dimensional neutral. I could require 2, 3, 4, or more dimensions, and I have to have someway of structuring my address to pull it out.
We could have some sort of looping mechanism. We get a list of coordinates in
@coordinates
, and then look at each coordinate. The last will point to data. The rest will simply be another reference to another array.It also may be possible to build a list of coordinates, and then evaluating it. Again completely untested:
If there were three coordinates, this would be
I'm not sure how a 1 dimensional array would work...
From there, you could build a statement and use
eval
on it and get the data.You'll have to have several methods.
This is more a brain dump, but it I think this might work. You don't have any set table dimensions and it might work for any N-dimensional table.
您可以使用 Text::TabularDisplay 来执行以下操作这。这是我对您的示例进行的快速试验。
显示:
我不确定这是否正是您正在寻找的。您确实必须通过将第一列留空来修改标题。
You may be able to use Text::TabularDisplay to do this. Here's a quick trial I did with your example.
shows:
I'm not sure if this is exactly what you were looking for. You do have to fudge with the header by leaving the first column blank.
Text::Table 在这里可能有用。我在下面展示了一个简单的示例:您可以使用该模块提供的各种选项来创建接近您所描述的内容。
输出
Text::Table might be useful here. I am showing a simple example below: You can play with the various options the module provides to create something close what you describe.
Output