索引对象点符号方法给出标量属性

Posted

技术标签:

【中文标题】索引对象点符号方法给出标量属性【英文标题】:indexed object dot notation method gives scalar property 【发布时间】:2013-07-05 03:41:47 【问题描述】:

我在使用点符号应用方法后尝试引用对象属性时遇到问题。 它仅在我尝试索引初始对象时发生

classdef myclassexample

properties
    data
end    

methods   
    function obj = procData(obj)            
        if numel(obj)>1
            for i = 1:numel(obj)
                obj(i) = obj(i).procData;
            end
            return
        end
        %do some processing
        obj.data = abs(obj.data);
    end
end
end

然后分配以下内容

A = myclassexample;
A(1).data= - -1;
A(2).data =  -2;

当调用整个数组并收集属性数据时,它工作正常

[A.procData.data]

如果我尝试索引 A,那么我只会得到一个标量

[A([1 2]).procData.data]

即使没有属性调用似乎也能正常工作

B  = A([1 2]).procData;
[B.data]

有什么想法吗?

【问题讨论】:

我无法复制这个;在这里可以正常工作...您使用的是哪个版本的 MATLAB? 2013a 相同的“问题”。但这有效[A([1 2]).data] 2013b 有同样的问题。一个有趣的观察是A.procData.('data') 也返回一个标量。 哇,这是 Matlab 的一个非常错误的角落。也会影响句柄类。更有趣的是:[A.procData().data] 访问非法内存并可能引发段错误 (Mac R2013a)。我认为返回值是缓冲区溢出的垃圾;有时它返回 [1,1] 但有时它是 [1,4e-309] 或其他类似的乱码。也发生在更简单的情况下,其中处理在 for 循环内。这绝对是 Mathworks 的一个错误,而且是一个糟糕的错误。 这里唯一真正的解决方案是submit a bug report to Mathworks。除此之外,问题本身的解决方法就是解决方案。 【参考方案1】:

我肯定会将此称为解析器中的错误;一个错误,因为它一开始并没有引发错误,而是允许您首先编写:obj.method.prop

MATLAB 在这种语法的某些变体中崩溃是一个严重的错误,绝对应该是 reported 给 MathWorks。

现在 MATLAB 中的一般规则是您不应该直接“对结果进行索引”。相反,您应该首先将结果保存到一个变量中,然后对该变量进行索引。

如果您使用func(obj) 形式而不是obj.func() 来调用对象的成员方法(dot-notation vs. function notation),这一事实就很清楚了:

>> A = MyClass;
>> A.procData.data       % or A.procData().data
ans =
     []
>> procData(A).data
Undefined variable "procData" or class "procData". 

相反,正如您所说,您应该使用:

>> B = procData(A):    % or: B = A.pocData;
>> [B.data]

FWIW,在使用普通结构和常规函数(与 OOP 对象和成员函数相反)时也会发生这种情况,因为无论如何您都无法索引到函数调用的结果。示例:

% a function that works on structure scalar/arrays
function s = procStruct(s)
    if numel(s) > 1
        for i=1:numel(s)
            s(i) = procStruct(s(i));
        end
    else
        s.data = abs(s.data);
    end
end

那么以下所有调用都会抛出错误(它们应该如此):

% 1x2 struct array
>> s = struct('data',1 -2);

>> procStruct(s).data
Undefined variable "procStruct" or class "procStruct". 

>> procStruct(s([1 2])).data
Undefined variable "procStruct" or class "procStruct". 

>> feval('procStruct',s).data
Undefined variable "feval" or class "feval". 

>> f=@procStruct; f(s([1 2])).data
Improper index matrix reference. 

您可能会问自己为什么他们决定不允许这样的语法。事实证明,MATLAB 不允许对函数调用进行索引(无需引入临时变量)是有充分理由的,无论是点索引还是下标索引。

以如下函数为例:

function x = f(n)
    if nargin == 0, n=3; end
    x = magic(n);
end

如果我们允许对函数调用进行索引,那么如何解释以下调用 f(4) 就会有歧义:

是否应该解释为:f()(4)(即不带参数的调用函数,然后使用线性索引对结果矩阵进行索引以获得第 4 个元素) 或者它应该被解释为:f(4)(调用一个参数为 n=4 的函数,并返回矩阵magic(4)

这种混淆是由 MATLAB 语法中的几件事引起的:

它允许仅通过名称调用不带参数的函数,而不需要括号。如果有函数f.m,您可以将其称为ff()。这使得解析 M 代码更加困难,因为不清楚标记是变量还是函数。

括号用于矩阵索引和函数调用。因此,如果标记x 表示一个变量,我们使用语法x(1,2) 作为矩阵的索引。同时如果x是一个函数的名字,那么x(1,2)就是用来调用两个参数的函数。

另一个混淆点是逗号分隔的列表和返回多个输出的函数。示例:

>> [mx,idx] = max(magic(3))
mx =
     8     9     7
idx =
     1     3     2

>> [mx,idx] = max(magic(3))(4)     % now what?

我们应该从 MAX 中返回每个输出变量的第 4 个元素,还是仅从第一个输出参数中返回第 4 个元素以及完整的第二个输出?当函数返回不同大小的输出时呢?

所有这些仍然适用于其他类型的索引:f()(3)/f(3)f().x/f.xf()3/f3

因此,MathWorks 决定避免上述所有混淆,并且根本不允许直接对结果进行索引。不幸的是,他们限制了过程中的语法。比如Octave就没有这个限制(可以写magic(4)(1,2)),不过话说回来新的OOP系统还在开发中,所以不知道Octave是怎么处理这种情况的。


对于那些感兴趣的人,这让我想起了另一个similar bug,关于包和类以及直接索引以获取属性。无论您是从命令提示符、脚本还是 M 文件函数调用它,结果都不同...

【讨论】:

让我着迷的是,Mathworks 为这些情况实现了制表符补全。当您单击选项卡以确定输出类型时,它们实际上会多次运行该方法。如果您将MyClass 扩展为handle,您实际上可以让它处理数据而无需按Enter 键——只需按Tab 键! (您也可以通过设置断点或打印一些输出并尝试制表符完成来看到这一点。)这很疯狂,但我认为这可能与他们对依赖 getter 的实现有关。 我想你是对的,就像他们通过在不应该的地方实现制表符完成来鼓励这种语法。如果你决定为你的班级重载 subsref 函数,情况会变得更糟(而处理标量和对象数组),我认为这是这个混乱的真正根源:) 这是一个related question,OP 要求利用此语法来启用 BDD 样式的单元测试:expects(1+1).toBe(2)。建议的解决方案之一是为该类实现subsref,但正如警告的那样,这肯定会破坏其他类型的下标索引。在使用对象数组时重载subsrefsubsasgn 几乎是不可能的,而不是同时保持默认提供的所有其他功能(使用诸如 obj(2).x3.y(4:6) 之类的表达式可能会变得非常笨拙) 很好的回应,@Amro。我只想补充一点,虽然必须先捕获变量才能对其进行索引,但您可以使用函数式方法来获取内联索引:in = @(x,varargin) x(varargin:) ; in(magic(3), 1, 2); @GordonBean:谢谢。是的,有很多方法可以获得这种内联索引,请参阅question 了解更多信息

以上是关于索引对象点符号方法给出标量属性的主要内容,如果未能解决你的问题,请参考以下文章

Pandas DataFrame to_json() 生成带点符号的索引

线程 Thread-5 中的异常:TypeError:只能将整数标量数组转换为标量索引

pandas基础

Python TypeError:只有整数标量数组可以转换为标量索引

如何使用我的反应应用程序中的 useState 钩子给出的 setState 方法增加状态对象的选定索引的计数

JVM14_Class文件结构细节魔数Class文件版本常量池访问标识(或标志)类索引|父类索引|接口索引集合字段|方法|属性表集合