【问题标题】:Fundamental limitations of cell arrays, arrays of structs, and scalar structs?元胞数组、结构数组和标量结构的基本限制?
【发布时间】:2018-04-13 23:00:29
【问题描述】:

几十年来,我一直在断断续续地使用 Matlab。我认为我对数组、结构、元胞数组、表、结构数组以及每个字段都是数组的结构有很好的把握。对于后两者,我假设每个字段都需要是统一类型。我发现不存在这样的限制:

也许多年来 Matlab 变得更加灵活(我使用的是 2015b),但如果我发现对每种类型的局限性的理解是错误的,它确实会削弱我为任务选择最佳变量类型的信心。出于这个问题的目的,我无法真正阐明任务的需求,因为我将大型待办事项分解为任务的方式取决于我对我可以使用的数据类型的理解,以及它们的优势/局限性.

我可以(并且已经)阅读了令人作呕的在线文档,虽然他们会引导您通过代码来说明数据类型能够做什么,但我还没有遇到关于单元格之间比较限制的简洁描述数组、结构数组以及其字段本身就是数组的结构——以至于我可以使用这些知识在给定情况下选择最佳结构。基本的东西,我确实发现,例如,相同的字段名称将出现在结构数组的每个结构中(但如上面的示例所示,每个结构的每个字段都可以包含高度异构的数据类型和/或数组大小)。

问题

谁能指出元胞数组、结构数组和其字段本身是数组的标量结构之间的限制比较?我正在寻找一种水平的处理方法,可以让编码人员在 (i) 速度、(ii) 内存和 (iii) 可读性、可维护性和可演化性之间做出最佳权衡。

我故意省略了表格,因为尽管我很喜欢它们方便地访问数据集和对数据集(及其呈现方式)进行子集化,但事实证明它们在处理数据方面相当缓慢。它们有它们的用途,我使用它们很随意,但出于比较的目的,我对它们不感兴趣,这是引擎盖下的算法编码。

【问题讨论】:

  • 你能缩小你的问题范围吗?是的,struct 数组的不同元素的相同字段不必属于同一类型。在cellarray 之间,主要是方便访问。两者都不喜欢(或需要)array 附带的那种优化。 dict 有利于方便地访问键值对。请注意 struct 的字段的相等性是通过引用。我还没有对cell 进行调查,但我会假设相同。
  • 至于table,我不确定它是否与array 一样优化矩阵操作。我无权访问它。但我在文档中看到table 允许您有更多选项输出到文件并与其他实用程序交互。
  • “对于后两者,我假设每个字段都必须是统一类型。我发现不存在这样的限制。”这种限制从未存在过。
  • @Argyll:dict 是 Octave 的东西吗? MATLAB 没有,至少在核心语言中没有。它可能是其中一个工具箱中的自定义类。 MATLAB 确实有 containers.Map,这是一个哈希表。 ||另外,“struct 的字段的相等性是通过引用”是什么意思?
  • @Argyll:感谢您提示澄清问题。我做了相应的修改。当您说结构之间的相等是通过引用时,我不确定您的意思(我熟悉引用与值的概念)。我承认,这些数据类型都不像基本数据类型的原始数组那样精简。关于 dict,我使用 document.map 作为哈希表。

标签: arrays matlab struct


【解决方案1】:

我认为您的问题最终会缩小到这三种“类型”的数据结构:

元胞数组、结构体数组和字段本身就是数组的结构体之间的比较限制

[请注意,“其字段本身就是数组的结构”我在这里翻译为“标量结构”。结构数组也可以包含任意数组。我希望我的想法在下面变得清晰。]

对我来说,这些并没有太大的不同。这三个都是异构数据的容器。 (异构数据是非统一数据,每个数据元素可能具有不同的类型和大小。)这些语句中的每一个都可以返回任何类型的数组,与容器中任何其他数组的类型无关:

  • 元胞数组:array{i,j}

  • 结构数组:array(i,j).value

  • 标量结构:array.value

所以这一切都取决于你想如何索引:

array(i,j).value
       ^     ^
       A     B

如果您只想使用 A 进行索引,请使用元胞数组(当然,您需要花括号)。如果您只想使用 B 进行索引,请使用标量结构。如果你想要AB,请使用结构数组。

据我所知,成本没有区别。这些容器中包含的每个数组都占用了一些空间。各种容器的空间开销是相似的,我从来没有注意到时间开销的差异。

但是,这两者之间有很大的不同:

array(i).value   % s1
array.value(i)   % s2

我认为这个问题也涉及这种差异。 s1s2 有更多的空间开销:

>> s1=struct('value',num2cell(1:100))
s1 = 
  1×100 struct array with fields:
    value
>> s2=struct('value',1:100)
s2 = 
  struct with fields:
    value: [1×100 double]
>> whos
  Name      Size             Bytes  Class     Attributes
  s1        1x100            12064  struct              
  s2        1x1                976  struct              

数据需要 800 字节,所以s2 有 176 字节的开销,而s1 有 11264 (1408%)!

原因不是容器,而是我们存储一个包含 100 个元素的数组,以及包含一个元素的 100 个数组。每个数组都有一个特定大小的标头,MATLAB 使用该标头来了解它是什么类型的数组、它的大小、管理它的存储和延迟复制机制。拥有的数组越少,使用的内存就越少。

所以,不要使用异构容器来存储标量!这些东西只有在您需要存储更大的数组或不同类型或大小的数组时才有意义。


未明确询问(并且在编辑后明确询问)的异构容器是表。表类似于标量结构,表的每一列都是一个数组,不同的列可以有不同的类型。请注意,可以将元胞数组用作列,允许将异构元素存储在列中,但如果不是这种情况,它们最有意义。

与标量结构的一个区别是每列必须具有相同的行数。另一个区别是索引可以看起来像元胞数组、标量结构或结构数组。

因此,表格对包含的数据施加了一些约束,这在某些情况下非常有益。

但是,正如 OP 所指出的,使用表比使用结构要慢。这是因为table 是一个自定义类,而不是像结构和元胞数组这样的原生类型。如果您在 MATLAB 中键入 edit table,您将看到源代码,它是如何实现的。这是一个classdef 文件,就像我们任何人都可以写的一样。因此,它具有相同的速度限制:JIT 没有针对它进行优化,索引到表中意味着运行编写为 M 文件的函数等。


还有一件事:不要创建结构的元胞数组,或使用元胞数组创建标量结构。这增加了容器的级别,从而增加了开销(空间和时间),并使内容更难使用。我在这里看到了与这种结构导致的数据访问困难相关的问题:

data{i,j}.value   % A cell array with structs. Don't do this!
data.value{i,j}   % A struct with cell arrays. Don't do this!

第一个例子相当于一个结构体数组(开销更大),除了无法控制每个单元格中的结构体字段。也就是说,其中一个单元格可能没有.value 字段。

仅当value 的大小与第二个结构字段的大小不同时,第二个示例才有意义。如果所有结构字段都是(应该是)像这样大小相同的元胞数组,则使用结构数组。同样,开销更少,一致性更高。

【讨论】:

  • 您正确地解释了我的问题。我已经相应地修改了原来的“帖子”。您的术语“标量结构”很好地描述了我的心理图景,因为结构本身没有排列 - 排列被下推到各个字段的级别。我知道您的s1 会在速度方面付出代价,所以我对它在内存方面付出代价并不感到惊讶,这可能是由于更精细的内部数据组织。我谨慎使用它,因为它与我对问题数据的看法更好地保持一致,从而提高了可读性。
  • 在您的代码中,我很惊讶s1=struct('value',num2cell(1:100)) 的行为与s1.value=num2cell(1:100) 不同,但可以肯定的是,这是文档中捕获的异常行为之一。我发现 Matlab 有很多这样的符号规则要记住,这是它长期发展的结果。
  • 我不完全确定您所说的“异构容器”是什么意思,但我猜这是指元素类型不必统一的数组,例如结构数组、单元格数组。需要明确的是,元素 表面上是“统一的”,因为它们是结构或单元格,但在结构的情况下,成员变量不必在整个数组中包含相同的数据类型数组;在元胞数组的情况下,内容根本不需要非常相似。
  • 奇数。以上3个cmets针对Cris Luengo。出于某种原因,stackoverflow 会从我的 cmets 中删除前导 @Cris Luengo: ,即使我返回并重新添加它们。
  • @user36800:帖子的作者总是会收到关于该帖子的 cmets 的通知,@-tag 可能已被删除,因为它不是必需的。我对帖子进行了一些编辑,希望能更明确地说明一些内容并解决您的执行时间问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-06
  • 2016-06-08
相关资源
最近更新 更多