角落情况,意外和不寻常的MATLAB

28
多年来,我读别人的 MATLAB 代码时,遇到并收集了一些 MATLAB 语法的例子,这些例子可能一开始看起来不太常见和直观。请随意评论或补充此列表。我用 r2006a 进行了验证。
MATLAB 总是将函数的第一个输出参数(如果至少有一个)返回到其调用者工作区,即使在没有返回参数的情况下调用函数,例如 myFunc1(); myFunc2();,调用者工作区仍将包含 myFunc2(); 的第一个输出参数作为“不可见”的 ans 变量。如果 ans 是引用对象,则它可能会发挥重要作用 - 它将保持活动状态。
set([], 'Background:Color','red')

MATLAB有时候非常宽容。在这种情况下,将属性设置为对象数组时,即使属性是无意义的,至少当数组为空时也可以工作。这样的数组通常来自harray = findobj(0,'Tag','NotExistingTag')


myArray([1,round(end/2)])

在IT技术中,有时使用end关键字可能会看起来不够规范,但有时它比使用length(myArray)更方便。


any([]) ~= all([])

令人惊讶的是,any([])返回falseall([])返回true。我一直认为allany更强。
编辑:对于非空参数,all()对于any()返回true的一部分值(例如真值表)也返回true。这意味着any() false意味着all() false。MATLAB在使用[]作为参数时违反了这个简单的规则。Loren也在博客中提到了这个问题
Select(Range(ExcelComObj))

程序化风格的COM对象方法分派。不要惊讶exist('Select')返回零!
[myString, myCell]

MATLAB在这种情况下会将字符串变量myString隐式转换为单元格类型{myString}。这样做是有效的,即使我没有预料到它会这样做。
[double(1.8), uint8(123)] => 2 123

另一个强制类型转换的例子。每个人都可能期望将uint8值强制转换为double,但是Mathworks有另一种观点。没有警告,这种行为非常危险。


a = 5;
b = a();

虽然看起来有些傻,但你可以使用圆括号调用一个变量。实际上这很有意义,因为通过这种方式你可以执行一个函数并给出其 handle。
语法Foo(:)不仅适用于数据,而且对于函数也是如此,如果作为Bar.Foo(:)调用,此时函数的输入参数将被传递为字符冒号':'
例如,令Bar.Foo = @(x) disp(x),现在调用Bar.Foo(:)会在MATLAB命令行窗口中打印字符':'
这个奇怪的功能在所有的MATLAB 7版本中都可以工作而没有警告。
a = {'aa', 'bb'
'cc', 'dd'};

令人惊讶的是,这段代码既没有返回向量也没有引发错误,而是仅仅使用代码布局定义了一个矩阵。这可能是古代遗留下来的。

编辑:非常方便的功能,请参见gnovice的评论。


set(hobj, {'BackgroundColor','ForegroundColor'},{'red','blue'})

这段代码的作用与您预期的相同。函数set接受结构作为其第二个参数是一个已知的事实,并且这种语法只需要使用cell2struct函数即可实现。
等价规则有时会令人意外。例如,'A'==65返回true(虽然对于C语言方面的专家来说这是显而易见的)。类似地,isequal([],{})返回如预期的falseisequal([],'')返回true
字符串-数字等价性意味着所有字符串函数也可以用于数值数组,例如在大数组中查找子数组的索引。
ind = strfind( [1 2 3 4 1 2 3 4 1 2 3 4 ], [2 3] )

MATLAB函数isnumeric()对于布尔值返回false。这感觉就像……假的 :-)


您知道哪些其他意外/不寻常的MATLAB功能?


1
我认为在这种情况下需要使用社区维基,因为这基本上是一个“编译列表”的投票问题。 - gnovice
1
@gnovice: 更改为社区维基 - Mikhail Poda
9
令人惊讶的是,任何([])返回false,而所有([])返回true。我不明白为什么会有什么惊讶的地方;我觉得这完全是显而易见的。 - Timwi
3
@Mikhail: 这不是Matlab的问题,而是日常语言的问题。想象一个空房间,“这个房间里的所有绵羊都是绿色的吗?”- 逻辑上的答案是,因为“否”意味着房间里存在至少一只非绿色的绵羊。然而,日常语言不允许使用“所有”的这种方式(并且普遍存在歧义和不精确性)- 在英语中问“所有的绵羊都是绿色的吗?”暗示着至少存在一只绿色的绵羊,使得这个问题在一个空房间里本身就没有意义。 - Roman Starkov
1
@Mikhail:我的晚到的$0.02。 all([])为真,any([])为假,原因与prod([])为1和sum ([])为0相同:它们是结合律的产物。如果一个操作f是结合律的,那么对于任何A、B、C,使得A = [BC],不变条件f(A)= f([f(B) f(C)]) 必须成立。(例如,如果“sum”要成为可结合运算,则列表的总和必须等于列表的“第一项”与“剩余列表的总和”的总和)。这包括B = A且C = []的情况,因此此不变条件强制定义... - kjo
显示剩余3条评论
3个回答

12

图像坐标 vs 绘图坐标 这个问题曾经困扰了我很长时间。

%# create an image with one white pixel
img = zeros(100);
img(25,65) = 1;

%# show the image
figure
imshow(img);

%# now circle the pixel. To be sure of the coordinate, let's run find
[x,y] = find(img);
hold on
%# plot a red circle...
plot(x,y,'or')
%# ... and it's not in the right place

%# plot a green circle with x,y switched, and it works
plot(y,x,'og')

编辑1

数组维度

变量至少有两个维度。标量的大小为[1,1],向量的大小为[1,n][n,1]。因此,ndims对于它们中的任何一个都返回2(事实上,ndims([])也是2,因为size([])[0,0])。这使得测试输入的维数有点麻烦。要检查1D数组,您必须使用isvector,0D数组需要isscalar

编辑2

数组赋值

通常情况下,Matlab在数组赋值时是严格的。例如:

m = magic(3);
m(1:2,1:3) = zeros(3,2);

抛出一个

??? Subscripted assignment dimension mismatch.

然而,这些有效:
m(1:2,1:2) = 1; %# scalar to vector
m(2,:) = ones(3,1); %# vector n-by-1 to vector 1-by-n (for newer Matlab versions)
m(:) = 1:9; %# vector to 'linearized array'

编辑3

错误大小的数组逻辑索引 祝你调试愉快!

逻辑索引似乎会调用find函数,因为您的逻辑数组不需要与索引数量相同的元素!

>> m = magic(4); %# a 4-by-4 array
>> id = logical([1 1 0 1 0])
id =
     1     1     0     1     0
>> m(id,:)  %# id has five elements, m only four rows
ans =
    16     2     3    13
     5    11    10     8
     4    14    15     1
%# this wouldn't work if the last element of id was 1, btw

>> id = logical([1 1 0])
id =
     1     1     0
>> m(id,:) %# id has three elements, m has four rows
ans =
    16     2     3    13
     5    11    10     8

Steve Eddins在评论中提到与Edit 3相关的内容:“我希望我能回到过去,阻止我们进行允许大小不匹配的逻辑索引的更改。” - Luis Mendo

8

不要列举奇怪的MATLAB语法例子,我会解释一些问题中我认为是有意义或预期/记录/期望行为的一些示例。

  • How ANY and ALL handle empty arguments:

    The result of any([]) makes sense: there are no non-zero elements in the input vector (since it's empty), so it returns false.

    The result of all([]) can be better understood by thinking about how you might implement your own version of this function:

    function allAreTrue = my_all(inArray)
      allAreTrue = true;
      N = numel(inArray);
      index = 1;
      while allAreTrue && (index <= N)
        allAreTrue = (inArray(index) ~= 0);
        index = index + 1;
      end
    end
    

    This function loops over the elements of inArray until it encounters one that is zero. If inArray is empty, the loop is never entered and the default value of allAreTrue is returned.

  • Concatenating unlike classes:

    When concatenating different types into one array, MATLAB follows a preset precedence of classes and converts values accordingly. The general precedence order (from highest to lowest) is: char, integer (of any sign or number of bits), single, double, and logical. This is why [double(1.8), uint8(123)] gives you a result of type uint8. When combining unlike integer types (uint8, int32, etc.), the left-most matrix element determines the type of the result.

  • Multiple lines without using the line continuation operator (...):

    When constructing a matrix with multiple rows, you can simply hit return after entering one row and enter the next row on the next line, without having to use a semicolon to define a new row or ... to continue the line. The following declarations are therefore equivalent:

    a = {'aa', 'bb'
    'cc', 'dd'};
    
    a = {'aa', 'bb'; ...
    'cc', 'dd'};
    
    a = {'aa', 'bb'; 'cc', 'dd'};
    

    Why would you want MATLAB to behave like this? One reason I've noticed is that it makes it easy to cut and paste data from, for example, an Excel document into a variable in the MATLAB command window. Try the following:

    • Select a region in an Excel file and copy it.
    • Type a = [ into MATLAB without hitting return.
    • Right-click on the MATLAB command window and select "Paste".
    • Type ]; and hit return. Now you have a variable a that contains the data from the rows and columns you selected in the Excel file, and which maintains the "shape" of the data.

1
我认为指出为什么“all([])”会让人困惑是有帮助的——这是因为“all”在日常语言中的用法与逻辑/编程中的用法不同。我已经在问题的评论中发布了一个例子。 - Roman Starkov
至少,连接不同类型的整数会抛出一个错误(r2010a) - Jonas

5

数组 vs 单元格

让我们先从一些基本语法开始。要创建一个包含元素 a, b, c 的数组,你可以写成 [a b c]。要创建一个包含数组 A, B, C 的单元格,你可以写成 {A B C}。到目前为止都很好。

访问数组元素的方法是这样的:arr(i)。对于单元格,使用的是cell{i}。还是很好理解的。

现在让我们来试着删除一个元素。对于数组: arr(i) = []。从上面的例子中推断,你可能会尝试使用 cell{i} = {} 来删除单元格中的元素,但这是一个语法错误。事实上,删除单元格元素的正确语法与数组相同:cell(i) = []

因此,大多数情况下,你使用特殊语法访问单元格,但在删除项目时,你使用数组语法。

如果你深入挖掘,你会发现实际上单元格是一个数组,其中每个值都是某种类型。因此,你仍然可以写成 cell(i),你将得到 {A}(一个只有一个值的单元格!)。cell{i} 是一种检索直接获取 A 的简写。

总之,我认为这些都不太美观。


6
单元格的行为非常一致:arr(i)返回数组的第i个元素。对于数字数组,第i个元素是数字标量;对于单元格数组,第i个元素是标量单元格;对于结构体数组,第i个元素是标量结构体。花括号用于“深入”单元格,就像fieldnames用于“深入”结构体一样。此外,就像对于结构体一样,您可以继续访问单元格内容的元素,例如cellArray{i}(1,2)。 - Jonas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接