哎哟! 哎哟!让我直接跳进去打断你,然后再继续沿着这条路走下去!
我知道您不是程序员,但在生活中的某个时刻(显然,这是您的!),您必须面对事实并成为一名程序员,无论多么短暂。所以要知道,编程并不是一门真正的科学,它是一门艺术,是一种工艺,如果你愿意的话,而且很容易出错。还要知道,在您之前有数以百万计的程序员为您铺平了道路,并发现了哪些方法最有效,哪些方法会导致某些灾难。
我将描述您的代码中存在的其中六个“通向某些末日的道路”。
首先是使用global
. 不要使用全局变量!!当然,它们适用于小而简单的事情,但更好、更易于管理、更耐用、更健壮、更不容易出错的传递数据的方法是手动进行。根据经验,创建所有顶级函数时,尽可能少地依赖其他函数/变量。这是因为全局变量在程序的状态和函数的输出之间创建了紧密的耦合,这使得重现任何错误变得困难,如果不是不可能的话,并且调试(实际上是任何程序员花费他/她大部分时间的)一个完整的噩梦. 此外,除了运行的功能之外的任何功能都可以更改它们,因此
function testMe
global a;
a = 5*rand;
someFunction;
b = 4*a; % ERROR! or...will it?
function someFunction
global a;
a = a/5;
if a < 0.5
someOtherFunction; end
function someOtherFunction;
global a;
a = {'my string'};
有时会工作,有时会失败。可能发生的更糟糕的事情的一个例子:
function testMe
global a, b;
a = 5; b = 6;
result = someCalculation;
result = a*b*result;
function someFunction
global a;
a = sin(pi/rand); % INTENTIONAL
% do LOTS of stuff here
for a = 1:10 % OOPS! unintentional use of variable name
% do stuff
if (some weird condition)
break; end
end
不会有错误,没有警告,什么都没有,但你的结果仍然是垃圾。随着你的函数变大(通常它们会变大),这个错误变得越来越难找到。花几天时间来发现这种错误并不少见。
在您的代码中,您还可以更改全局变量a
和b
循环内部。这意味着任何使用a
and的函数/脚本,在这个完成后被b
调用,都会看到and 。现在假设你在这些循环中调用了一个函数,它改变了. -loop的下一次迭代的值是多少?假设你也得到错误的结果。您将如何发现该错误?a=10
b=10
a
a
a
出于显而易见的原因,这样的代码通常被称为“意大利面条代码”。也许它会起作用,并且易于编码,但最终它总是会大大减慢您的速度(更不用说继承您的代码的人了)。
防止大部分情况发生的更好方法是在更大的容器中收集数据,并明确地传递它们。假设我们使用 astruct
作为数据a-l
:
data = struct(...
'a', a,...
'b', b,...
'c', c,...
'd', d,...
'e', e,...
'f', f,...
'g', g,...
'h', h,...
'l', l);
这样你就可以说
result = myFunction(data);
访问内部数据myFunction
是这样的:data.a
获取 的值a
或data.f
的值f
等。说data.k = 5;
inmyFunction
不会改变,result
或传递给函数的原始data
值 - 您已经打破了紧密耦合并防止了所有上述问题。
在 Matlab 命令窗口中键入help struct
或help cell
以了解这些类型的通用容器。
列表中的第二个是使用变量 name l
。这有点傻,我可以简短地说:不要那样做:) 与大多数人(甚至一些程序员)所相信的相反,你只写了一行代码,但你读了数百甚至数千次。最好的做法是让阅读尽可能简单,而不是写作。l
只是看起来像,1
不是吗?错误k=1
vs比vsk=l
更难发现。k=m
k=1
名单上的第三个transpose
是关键字。这有点冗长,不是吗?在数学中,您将使用 A T,这比一直写完整定义更容易理解:
B = { A ij ➝ A ji ∀ i < m ⋏ j < n
你通常只是说 B = A T。在 Matlab 中也一样。矩阵的transpose
可以这样完成:
Actrans = A' ; % conjugate transpose
Atrans = A.'; % regular transpose
这将您的代码减少到不那么冗长
A = [1 3;3 2];
B = [a 0;0 b];
C = [d 0;e f];
D = [sqrt(d) 0;0 sqrt(f)];
E = C.'/D;
K = A+E;
M = E*D*E.';
排在第四位的是平等K==M
。因为它在这里,K
并且M
是矩阵。表达式K==M
是按元素计算的,原因在你以后的编程生涯中会变得很明显:) 这意味着这K==M
将再次是一个矩阵,大小与K
and相同M
,包含如果和中的0
对应元素不相等,以及如果这些元素是平等的。那么,-statement 将如何处理这样的矩阵呢?在 Matlab 中,只要第一个元素为真(在我看来,它应该抛出一个错误,但是哦,好吧)。K
M
1
if
true
这显然不是你想要的。我认为你想要的是两个矩阵中的所有元素都是平等的。最好使用这个:
if all( abs(K(:)-M(:)) < eps )
其中(:)
-notation 表示矩阵K
并且M
应该在比较之前扩展为列向量。这是因为all()
在一个维度上起作用,所以all(K==M)
仍然是一个矩阵(实际上是向量,但对于同一事物的特殊情况,这是一个不同的名称)。请注意,我没有使用相等 ( ==
),而是检查它们的差异是否小于某个微小值 ( eps
)。这是因为在浮点运算(所有计算机都使用)中,乘法和平方根之类的运算通常会受到舍入误差和近似/插值误差之类的影响。平等是一个非常艰难的要求,太难以评估true
在大多数情况下,它在数学上应该。您可以通过将两者的差异与与舍入误差 ( ) 相关的微小值进行比较来防止这种检测相等性的失败eps
。
列表中的第五个是你打印东西的方式。该print
声明本身会向系统的默认打印机发送一个数字,你知道,如果今天感觉要合作,那台喜怒无常的机器会吐出带有墨水的纸 :) 现在,我假设你试图在屏幕。像你开始显示事物的方式那样做并不是最好的方式:你会得到这个未命名的非结构化值列表的十几倍:
1 % which would be the value of 'a'
1 % which would be the value of 'b'
3 % which would be the value of 'd'
4 % which would be the value of 'e'
5 % which would be the value of 'f'
...
只看到出现的值会使阅读和解释正在发生的事情变得相当乏味。最好使用更具描述性的东西:
if all( abs(K(:)-M(:)) < eps )
% option 1
a
b
d % NOTE: not terminating with semicolon
e
f
% option 2
fprintf(...
'a: %d\n, b: %d\n, d: %d\n, e: %d\n, f: %d\n\n', a,b,d,e,f);
end
选项 1 只会显示
a =
1
b =
1
etc.
这至少还显示了变量的名称及其值。选项 2 更好:
a: 1
b: 1
d: 3
e: 4
f: 5
a: 1
b: 2
d: 3
e: 4
f: 5
etc.
(顺便说一句,循环中的值a,b,d,e,f
永远不会改变,那么为什么要首先显示它们呢?)
列表中的第六个(也是最后一个!)是特定于 Matlab 的一个:for
-loops。Matlab 是一种解释型、基于矩阵的语言。它的矩阵性质只是意味着每个变量本质上都是一个矩阵。解释意味着您的代码不会直接被计算机的处理器看到,它必须经过一系列解释和翻译才能计算出任何东西。这枚硬币有两个面:
- 它可以加快速度(比如编码,或者做一些“琐碎”的事情,比如求解线性系统、FFT、矩阵比较等)
- 它可以减慢速度(比如重复执行语句,比如在循环中)
考虑到性能,for
-loop 在 Matlab 中因使操作变得爬行而臭名昭著。在 Matlab 中采用的方法通常是矢量化代码,例如,使用所有变量都是矩阵的事实,并对其使用矩阵/张量运算而不是循环。在大多数编程语言中,这不是一种非常常见的方法(在不习惯它的程序员中,你会看到很多强烈的、激烈的抵制),但在数学环境中,它很有意义。在诉诸 for 循环之前,始终尝试使用矩阵/张量运算作为第一道攻击线(Matlab 有很多,请注意!)。
所以,这就是你的代码的问题:) 哦,是的,正如 Andreas Hangauer 已经提到的,将引用a
through的语句l
以及所有需要重新计算的语句放在循环中,你会没事的。