罗兰关于MATLAB的艺术

将想法转化为MATLAB

值语义的值

我很高兴欢迎Dave Foti回来,他将研究一些在MATLAB对象中值语义工作的方法,以及如何最小化数据拷贝。

我知道你们中的一些人可能有孩子在家远程学习,我想我要分享一个MATLAB对象,我做的,来帮助我的儿子练习乘法。虽然有可能将其构建到一个应用程序中,但我想做纸质测试,所以这个例子是设计来做一个可以打印的测试。你们中的一些人可能很熟悉MATLAB中的句柄对象或者可能用过其他语言,对象总是引用。我想用我的示例类来指出在MATLAB中句柄或引用语义通常是不必要的一些原因。

内容

处理和价值观

首先,我认为句柄是一个对象,它可以访问一些数据,这些数据不是对象本身的一部分,但可以从对象访问。传递这个对象时,我传递的不是实际的数据,而是一个ID,它提供了访问该数据的方法。这意味着当我更改对象拥有的数据时,我可能已经将该ID传递给了许多函数。不同地方的许多变量现在可以访问相同的数据并查看这些变化。将现实世界中的对象表示为句柄通常是有意义的,因为您不能只是复制现实世界中的对象。例如,如果我有一个旨在跟踪火车位置的Train对象,它可能看起来像这样:

classdef火车<处理属性数字的位置结束方法函数p = Train(Number, Location);p.Location =位置;结束结束结束

无论我使用多少变量来存储相同列车的句柄,我总是看到相同的状态。

X =火车(5402“波士顿”);Y = X;我们可以复制车柄或车号,但不能复制列车本身Y.Location =“纽约”;X.Location
ans =“纽约”

我所说的值或值语义的意思是,当我将一个对象传递给一个函数时,函数会接收该对象中包含的所有数据的副本。类似地,当我将一个对象赋给一个新变量时,该变量将获得所有数据的副本,尽管两个变量可能保存相同的数据,但一个变量的对象中的数据不会改变另一个变量的数据。虽然数字是最常见的值形式,但许多类型的对象都可以从相同的值语义中受益。以我的算术测试为例,它只是一个参数和算术问题的集合。

测试=相乘(“NumProblems”, 50岁,“马克斯”, 10)
test = MultTest with properties: NumProblems: 50 Max: 10 X: [50×1 double] Y: [50×1 double] Z: [50×1 double] OpSymbol: '×'

现在,如果我想做一个新的测试,除了从1到12而不是从1到10,我可以给一个新变量赋值测试然后在不改变的情况下修改它测试

test12 =测试;test12。马克斯= 12; test12 = drawProblems(test12);重新创建并画出问题

我原来的测试没有改变:

测试
test = MultTest with properties: NumProblems: 50 Max: 10 X: [50×1 double] Y: [50×1 double] Z: [50×1 double] OpSymbol: '×'

语义拷贝在MATLAB中是有效的

有时,人们担心复制所有这些对象会占用大量内存。重要的是要认识到MATLAB在许多方面优化副本,以使它们有效。当一个对象从一个变量复制到另一个变量时,MATLAB只是记录这个事实,但实际上没有数据被复制。这两个变量实际上引用的是同一个对象,但它知道它链接到多个变量。这并不重要,直到其中一个变量被用来修改对象。即使这样,MATLAB也会对对象做一个浅拷贝,这意味着对象内部的任何不被更改的矩阵都保持共享。当一个属性被修改时,只会复制该属性的数据,而不会复制未被修改属性的数据。如果我们再看一下上面的例子:

test12 =测试;test12。马克斯= 12;

test12。马克斯更改时,不会复制问题,因为这些数组仍然可以在对象之间共享。当我们重新绘制问题时,只有到那时,才会为新问题创建新的数组。对于这些简单的示例,您不会注意到差异,但是当对象存储非常大的数组时,有效的复制会产生真正的差异。

值语义是明确的输入和输出

我一直欣赏的MATLAB的一个特性是,你可以看到一个函数调用,很容易地告诉输入和输出是什么,即使一个变量既是输入又是输出。一些语言和系统使用输入作为输出,您必须阅读函数文档或输入参数注释,如“in”、“out”和“in/out”,以了解参数是如何使用的。在MATLAB中,输入在右边,输出在左边,输入/输出的参数同时出现在左边和右边。当变量在函数调用的左右两边使用时,MATLAB通常可以避免复制,即使变量被函数修改。让我们看看arithmetestdrawproblems函数的前几行:

dbtypeArithmeticTest.m45:50
47 t 48 withAnswers (1,1) logical = false;49 end 50 t = genProblems(t);

在第45行,我们可以看到t既是输入又是输出。这意味着MATLAB有时可以在适当的地方修改t而不做复制,但只有在对函数的调用使用相同的变量作为输入和输出时才能这样做。我们可以在第50行看到这样一个调用不同方法的示例。即使通过这些多层函数调用,也有可能t从来没有实际复制过,因为MATLAB可以告诉它永远不需要多个副本来保留值语义。

以下是完整的项目列表供您参考:

我想听听你们在MATLAB中创建的新值类型的例子。请发布任何你想分享的描述或链接在这里

附录:代码文件

classdefArithmeticTest% arithmetest算术测试的抽象基类属性%测试中问题的数量NumProblems (1,1) {mustBeReal, mustBeInteger} = 35;%限制操作数的值Max (1,1) {mustBeReal, mustBeInteger} = 20;结束属性(SetAccess = protected结束方法(摘要)t = genProblems(t)结束方法函数t = ArithmeticTest (params)属性的所有名称值对正在构造的%对象。S = string(fieldnames(params)') t.(S) = params.(S);结束结束结束方法(访问=保护)函数op = getSymbol(t, k)如果isscalar(t.OpSymbol) op = t.OpSymbol;其他的op = t.OpSymbol (k);结束结束结束方法把问题画在一个可以打印出来的图形中% drawProblems(t, true)将打印问题和答案%填写。函数t = drawProblems(t, withAnswers)参数t withAnswers (1,1) logical = false;结束t = genProblems (t);如果(t.NumProblems <= 48) jN = 6;kN = 8;字形大小= 20;axPos = [0 0 7 10];elseif(t.NumProblems <= 80) jN = 8;kN = 10;字形大小= 12;axPos = [0.55 0 8.5 10.4];其他的约= 10;kN = 10;字形大小= 12;axPos = [0.55 0 8.5 10.4];结束图(f =“单位”“英寸”“位置”, [3 0 8.5 11]);一个=轴(“父”f“单位”“英寸”“位置”, axPos);a.Visible =“关闭”;a.XLim = [0 jN+1];a.YLim = [0 kN];n = 1;j = 1:约k = 1: kN如果(n > t.NumProblems)打破结束x = t.X ((j - 1) * kN + k);y = t.Y ((j - 1) * kN + k);如果(x < 99) && (y < 99) dx = 2;dy = 2;其他的dx = 3;dy = 3;结束文本(j - tx1 =。8 k。3...sprintf (“%”+ dx +“d \ n % s %”+ dy +“d”...x, t.getSymbol (n), y));tx1。字体名=“明星控制台”;tx1。字形大小=字形大小;tx1。翻译=“没有”如果(x <99) && (y<99) underetxt =“___”其他的underlineTxt =“____”结束文本(j - tx2 =。8 k -。44,underlineTxt); tx2.FontName =“快递新”;并。字形大小=字形大小;并。翻译=“没有”如果(withAnswers) tx3 = text(j-。8 k -。7,sprintf (“%”+ dx +“d”...t.Z ((j - 1) *约+ k)));tx3。字体名=“快递新”;tx3。字形大小=字形大小;tx3。翻译=“没有”结束N = N + 1;结束如果(n > t.NumProblems)打破结束结束结束结束结束classdef乘< ArithmeticTest* * * *乘法题测试方法函数t =相乘(params)参数参数个数。乘结束t@ArithmeticTest (params);t.OpSymbol = char (215);% Unicode编码点00D7t = drawProblems (t);结束函数t = genProblems(t) rng(“洗牌”);从所有相乘的组合开始% number from 1…t.Max。A1 = 1: t.Max;A2 = 1: t.Max;X = 0 (t。马克斯^ 2,1);Y = 0 (t。马克斯^ 2,1);k = 1: t。马克斯j = 1: t。马克斯X((k-1)*t.Max+j,1) = A1(k); Y((k-1)*t.Max+j,1) = A2(j);结束结束减少10题或1题的难度。idx = find(X == 10);X (idx (1:5)) = [];Y (idx (1:5)) = [];idx = find(Y == 10);X (idx (1:5)) = [];Y (idx (1:5)) = [];idx = find(X == 1);X (idx (1:5)) = [];Y (idx (1:5)) = [];idx = find(Y == 1); X(idx(1:5)) = []; Y(idx(1:5)) = [];随机排列问题,确保我们有%不够的问题。numProbs =元素个数(X);numSets =装天花板(t.NumProblems / numProbs);t.X = 0 (t。NumProblems, 1);t.Y = 0 (t。NumProblems, 1);如果numSets > 1k = 1:numSets-1 idx = randperm(numProbs, numProbs);t.X ((k - 1) * numProbs + 1: k * numProbs) = X (idx);t.Y ((k - 1) * numProbs + 1: k * numProbs) = Y (idx);结束其他的k = 0;结束idx = randperm(numProbs, numProbs);t.X (k * numProbs + 1:结束)= X (idx (1: t.NumProblems-k * numProbs));t.Y (k * numProbs + 1:结束)= Y (idx (1: t.NumProblems-k * numProbs));%计算产品s manbetx 845t.Z = t.X .* t.Y;结束结束结束




发布与MATLAB®R2020a

|

评论

要留下评论,请点击在这里登录到您的MathWorks帐户或创建一个新帐户。