教程:为什么变量不应该动态命名(eval)

3,494次浏览(过去30天)
斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑: 斯蒂芬。 2021年8月12日
简介:
动态访问变量名会对代码的可读性产生负面影响,并且会阻止MATLAB对其进行优化,从而导致代码运行速度变慢。最常见的替代方法是使用简单高效的方法 索引
解释:
有时初学者(和一些自学成才的教授)认为动态创建或访问变量名是一个好主意,变量通常是这样命名的:
  • matrix1matrix2matrix3matrix4,……
  • test_20kmhtest_50kmhtest_80kmh,……
  • nameBnameC命名,……
理由很充分 应该避免使用动态变量名 (点击链接跳转到下面的“答案”):
有很多 更好的选择 访问动态变量名:
注意避免 eval (和 assignin ,等等)并不是一些晦涩难懂的MATLAB限制,它也适用于许多其他编程语言:
MATLAB文档:
如果您对阅读下面的答案不感兴趣,那么至少要阅读MATLAB自己关于这个主题的文档 替代方案eval函数 ,表示 “eval函数的一个常用用法是创建一组变量,如A1, A2,…,An, but this approach does not use the array processing power of MATLAB and is not recommended. The preferred method is to store related data in a single array." 可以非常有效地访问单个数组中的数据 索引
请注意,所有这些问题和缺点也适用于函数 负载 (没有输出变量), assignin evalin , evalc , MATLAB文档明确建议 避免这样的功能evalevalcevalin,函数宏指令(帧)
MATLAB官方博客对此进行了解释 为什么eval应该避免 越好 替代eval ,很明显 建议不要神奇地创建变量 .使用 eval 从这个位置出来 一号 在这个列表中 十大让我哭的MATLAB代码实践 .熟练使用MATLAB 建议避免使用eval对于简单的代码 , 你写过很多关于这个话题的文章吗
7评论
史蒂文的主"class=
史蒂文的主 2019年10月24日
斯蒂芬,考虑到这一页已经写了这么长时间,我建议用一个 BLUF 在问题的顶部避免 博士TL; 综合症,大致如下:
底线: 对变量进行编号会对代码的可读性产生负面影响,并且会阻止MATLAB像使用替代技术那样优化代码,从而导致代码运行速度变慢。最常见的替代方法是:
[项目符号列表,包含不超过3个常用替代方案的简短描述]
欲了解更多信息,请参阅本页剩余部分的详细讨论。”
我知道我自己可以做到,但您已经投入了大量精力来创建和更新这个页面。

登录评论。

接受的答案

斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2017年12月11日
2的评论
斯蒂芬。"class=
斯蒂芬。 2018年7月15日
@Cris Lengo:你仍然可以在这里访问新闻阅读器线程:
有关案文是:
MATLAB没有骗你。
运行函数时,MATLAB需要确定每个标识符
您使用is作为解析函数过程的一部分。那时,
在你的代码中没有指示debug应该是一个变量;然而,
有一个名为debug的函数。因此,MATLAB确定
代码中的debug实例应该调用该函数。当
代码被实际执行,一个名为debug的变量被创建,而WHICH
反映了这一事实——但在这一点上,对MATLAB来说“改变”已经太晚了
它的思想”,并试图在最后一行调用调试函数。调试
是一个脚本文件,因此您正确地接收到一个错误。
这就是为什么你不应该在运行时“噗”的变量到工作空间,
无论是通过EVALIN, ASSIGNIN, EVAL或LOAD。
--
史蒂夫的主

登录评论。

更多答案(19)

斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2019年4月18日
MATLAB文档 eval函数的替代品 解释使用的代码 eval 比较慢是因为 MATLAB®在您第一次运行代码时编译代码,以提高未来运行的性能。但是,由于eval语句中的代码可以在运行时更改,因此不会编译。”
MATLAB使用 JIT 加速工具,在代码执行时进行分析,并优化代码以更有效地运行。当 eval 使用JIT优化是无效的,因为每个字符串都必须被编译并在每次迭代中再次运行!这就形成了一个循环 eval 非常缓慢。这也是为什么不公正 创建 变量用动态变量名是慢的,但也慢 访问 他们。
即使是 eval 隐藏在 str2num 可以减慢代码:

斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2018年2月28日
安全风险
eval 将计算任何字符串,无论它包含什么命令。你觉得这样安全吗?这个字符串命令可能是恶意的,或者只是一个错误,但它可以做到这一点 任何东西 在你的电脑上。你会运行可以的代码吗 做任何事 你的电脑,却不知道它要做什么?
对于一些初学者来说,令人惊讶的答案是“是的!”。
初学者会很高兴地运行可以做到的代码 任何事情 用他们的电脑。例如,尝试运行这个(taken from 这是乔斯的回答 ):
eval (char (”fkur *) Ykvj GXCN”{qw“pgxgt mpqy“yjcv”jcrrgpu0“伏特”eqwnf hqtocvvgf“jcxg {qwt“jctfftkxg”000)+2))
即使你不知道它会做什么,你真的在你的电脑上运行它吗?每当初学者编写代码获取用户输入并对其求值时,他们就赋予了用户运行的能力 任何事情 .你觉得这样安全吗?
3评论
史蒂文的主"class=
史蒂文的主 2019年10月23日
运行 字符 命令是安全的,这将创建一个 字符 你可以读向量。删除 eval () 字符 命令,它将不会执行存储在 字符 向量。

登录评论。


斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2019年5月4日
难以共事
许多初学者来这里的问题基本上是“我有很多编号的变量,但我不知道如何做这个简单的操作……”,或者“我的代码非常慢/复杂/……”:
甚至倡导 eval 对它感到困惑,不能让它正常工作,甚至不知道为什么,正如下面两个例子清楚地表明的那样:
为什么他们不明白为什么它不起作用?:
  • 完全混淆的代码。间接代码评估。
  • 代码辅助工具不要工作。
  • 语法高亮显示不起作用。
  • 静态代码检查不起作用。
  • 没有有用的错误消息,等等

斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2019年12月30日
使用 eval 这使得追踪错误变得非常困难,因为它混淆了代码并禁用了许多 代码辅助工具 .为什么初学者 想要 使用能成功的工具 困难 让他们修改代码?
这里有一些例子来说明应该如何 简单的操作 因为使用的选择而变得非常难以调试 eval
基于导入的数据或用户输入动态生成变量名的代码也容易达到名称长度限制:
这句话 调试总结 eval 基于代码: “我自己从来没有尝试过使用它,但它似乎会创建难以阅读、无法调试的代码。如果你看不懂它,也修不好它又有什么用呢?” 请注意, eval 同样邪恶的兄弟姐妹 evalc evalin 而且 assignin 也会使代码变慢和有bug:

斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2018年2月4日
模糊的代码意图
什么 这段代码 做什么?:
X1 = [119,101,98,40,39,104,116,116,112,58,47,47,119,119,119];
X2 = [46,121,111,117,116,117,98,101,46,99,111,109,47,119,97];
x3 =[116、99104、63118、61100、81119、52119、57、87103、88];
x4 =(99年,81年,39岁,44岁,39岁,45岁,98114111119115101114年,39岁,41);
eval (char ((x1, x2, x3, x4)))
不幸的是 eval 这使得编写难以理解的代码变得容易:它不清楚 什么 它有,或者 为什么 .如果您在不知道代码是做什么的情况下运行该代码,那么您应该知道它 可以 删除了你所有的数据,或者给你所有的联系人发了电子邮件,或者从互联网上下载了任何东西,或者更糟……
因为 eval 容易隐藏 意图 在这些代码中,许多初学者最终编写的代码非常难以遵循和理解。这使得代码充满bug,并且更难调试!请看这些例子:
正确编写的代码是清晰和可理解的。清晰易懂的代码更容易编写、修复和维护。人们阅读代码的次数比编写代码的次数要多,所以永远不要低估编写清晰易懂的代码的重要性:编写代码注释,编写帮助部分,使用一致的格式和缩进等等。
3评论
沃尔特·罗伯森"class=
沃尔特·罗伯森 2019年7月24日
我刚刚遇到有人在计算变量名上使用num2str(),以便在不直接在代码中使用eval()的情况下具有eval()的效果。不用说,这是一个模糊的意图。

登录评论。


斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2019年1月6日
代码助手工具不起作用
MATLAB编辑器 包含许多高级用户使用的工具 不断 使用,初学者在学习使用MATLAB时应该特别感激(提示:学会使用它们!)。然而 没有一个 这些工具中的任何一个都可以使用 eval
注意这些 不要工作 当使用 eval evalc ,等等来神奇地创建或访问变量名 .您希望禁用帮助您编写功能代码的工具吗?这里有一些例子 eval 隐藏代码错误并使调试代码变得困难:
1评论
汤姆·霍金斯"class=
汤姆·霍金斯 2019年2月7日
关于这个话题,如果代码分析器和 checkcode 会在什么时候发出警告 eval 等等。也许这样就能减少关于他们的问题了?

登录评论。


斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2018年7月8日
可供选择:索引到单元格数组或nd -数组
通常当用户想要使用 eval 他们试图创建有编号的变量,这实际上是一个 指数 连在名字上。那为什么不简单地把它 -index into a 真正的 index: MATLAB在处理索引时非常快速和高效,使用索引将使代码比任何涉及动态变量名的代码都简单得多:
使用 ND-arrays 是一种特别有效的处理数据的方法:可以在完整的数组上执行许多操作(称为 向量化代码 ),并且nd -array很容易获取数据,并减少了错误的机会:
或者简单地把数据放到a的单元格中 单元阵列
还有一些真实世界的例子,索引要简单得多 eval


斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2019年10月21日
选择: 负载 进入结构,而不是进入工作区
在几乎所有以编程方式导入数据的情况下(即不只是在命令窗口中玩),建议使用 负载 数据转换为输出参数(是一个 结构 如果文件为 .mat 文件):
S =负载(...);
结构的字段可以直接访问,例如:
S.X
S.Y
或者使用 动态字段名 .注意,这是的倒数 保存标量结构的字段
重要的是要注意(与许多初学者的想法相反),在循环中实际上要容易得多 保存 而且 负载 中的变量名时的数据 .mat 文件 不要改变 ,因为必须在每个文件中处理不同的变量名,实际上会使保存/加载文件变得更加困难。
总结:当使用循环时,保持所有变量名相同!
下面是载入变量的真实例子:
最后是史蒂文·洛德的评论 负载 -ing直接进入工作区:


斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2017年12月18日
其他语言:请勿使用 eval
如果你认为避免动态变量名只是一些“奇怪的MATLAB事情”,这里是对其他一些编程语言的相同讨论,所有建议 “不要创建动态变量名”
有些语言可能会使用、要求或以其他方式鼓励动态变量名。如果这就是他们高效工作的方式,那就这样吧。但是,在一种语言中有效的方法在其他语言中并不意味着相同的方法……如果您希望有效地使用MATLAB,使您的代码更容易使用,并以其他MATLAB用户会欣赏的方式编写,那么您应该学习如何使用MATLAB的特性和工具:

斯蒂芬。"class=
斯蒂芬。 2017年7月19日
编辑:斯蒂芬。 2019年1月6日
在工作区中神奇地出现变量是有风险的
首先,这些变量会在没有警告的情况下被覆盖,导致难以发现的错误。但还有一个更严重但微妙的问题,这是由MATLAB解析器寻找替代函数/对象/…调用这些而不是使用神奇创建的变量:基本上,如果变量 不存在 然后是解析器 做到最好 为了找到与该名称稍后被调用/使用的地方相匹配的东西…它可能会找到一些东西!例如:
解决方法很简单:不要神奇地让变量“噗”地出现:总是这样 负载 不要动态地创建变量名。
6个评论
沃尔特·罗伯森"class=
沃尔特·罗伯森 2019年8月21日
目前,每当MATLAB路径发生变化时,包括cd()时,都会进行一些名称重新解析——这是避免在代码中使用cd()的一个原因。
过去,为了有效地处理这种情况,我曾考虑过必须建立什么样的结构。不过,我并没有坚持到底;只是一些思维实验。

登录评论。


斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2019年3月4日
混淆数据和代码
在变量名中包含数据和元数据(例如,用用户的输入命名变量,测试主题的名称,或者(非常常见的)在变量名上添加索引)是一个微妙的(但密切相关的)问题,绝对应该避免。
在变量名中包含元数据的问题是,这打破了代码存在的概念 广义 ,因为它混合了 代码 而且 数据 在一起。事实上,数据和代码应该分开来保持代码的通用性。编写为。的代码 尽可能一般 更简单,更健壮,适应性更强,更容易编写,也更容易修复,这反过来使代码的bug更少。混合 元- 将数据转换为变量名实际上只会使一切变得更加复杂,这反过来又会使代码变慢和充满bug。
阅读这些讨论,了解为什么将数据和元数据放在变量名中是一种糟糕的做法:
在很多情况下,元数据只是一个 实际 索引,即禁止数据顺序的值。但在这种情况下 实际 指数应该变成一个更有效率的 真正的 数值指数:

斯蒂芬。"class=
斯蒂芬。 2016年9月26日
编辑:斯蒂芬。 2017年1月31日
替代方案:使用更有效的方法在工作空间之间传递变量(适用于 evalin assignin 等)
使用 嵌套函数 ,或传递参数,或使用任何其他有效的方法在工作区之间传递数据:

斯蒂芬。"class=
斯蒂芬。 2017年11月30日
编辑:斯蒂芬。 2018年10月26日
PS: eval 没有故障:
一些初学者显然是这么认为的 eval (和朋友)一定是错误的,应该从MATLAB中一起删除。他们会问"如果 eval 都碎成这样了,为什么还不移走?”但是重要的是要理解这个问题是由神奇地访问变量名引起的 不管使用什么工具或操作 ,以及 eval (或 assignin ,或 evalin ,或 负载 没有输出参数等)只是被不恰当地使用了,因为有很多 更好的 可用的方法( 更好的 从更快,更整洁,更简单,更少漏洞等意义上来说)。阅读下面的讨论,你会发现一些关于这种困惑的好例子:
注意这一点很重要 任何 一种语言的特性可以被低效或不恰当地使用,而不仅仅是 eval ,这是 语言本身可以控制的东西。例如,很常见的情况是,有人可能会用慢循环来解决问题,而没有 预分配输出数组 :这是 意味着 循环是“错误的”,需要从MATLAB中删除!
程序员需要编写高效的代码。

斯蒂芬。"class=
斯蒂芬。 2019年4月17日
编辑:斯蒂芬。 2019年4月17日
选择: 保存 标量结构的场
保存 命令具有一个选项,用于将标量结构的字段保存为对象中的单独变量 .mat 文件。例如,给定一个标量结构:
S.a = 1;
S.b = [2,3];
这将保存变量A和B在.mat文件中:
保存(“myfile.mat”“结构”“年代”
这是的逆函数 加载到结构中 .一些线程展示了如何使用它:

史蒂文的主"class=
史蒂文的主 2019年4月30日
编辑:史蒂文的主 2019年4月30日
替代方案:使用 表格 时间表 数组
表格 (在R2013b版本中引入)和 时间表 (在R2016b版本中引入)数组允许您使用可以访问数据的行名和/或列名存储数据。例如,如果您创建了一个 表格 变量包括年龄、性别、身高、体重和吸烟者,以及以患者姓氏命名的行:
负载病人
患者=表(年龄,性别,身高,体重,吸烟,...
“RowNames”、LastName);
你可以问前五个病人的所有年龄:
患者(1:5,“年龄”
或所有姓史密斯或琼斯的病人的数据:
病人({“史密斯”“琼斯”},:)
属性中添加新变量 表格 ,通过硬编码变量的名称
注明患者身高是否超过5.5英尺。
病人。veryTall =病人。身高> 66
或者使用存储在 字符 字符串 变量。下面的代码示例在患者中创建了名为over40和under35的新变量 表格 使用不同的索引技术。
newname1 =“over40”
Patients .(newname1) =患者。年龄> 40;
newname2 =“under35”
Patients {:, newname2} = Patients。年龄< 35岁;
:患者(1:10)显示前十行
下面的代码示例选择Height或Weight,并使用动态名称为第5到第10个患者显示所选变量。
如果兰特> 0.5
selectedVariable =“高度”
其他的
selectedVariable =“重量”
结束
病人。(selectedVariable) (5:10)
看到 本文档页 类中可用于访问和操作数据的技术的详细信息 表格 时间表 数组中。 本文档页 控件中的数据访问信息 时间表 使用与行关联的时间信息。
1评论
斯蒂芬。"class=
斯蒂芬。 2019年4月30日
这是一种更简单的生成表的方法 .mat 文件:
S =负载(“patients.mat”);
T = struct2table(S,“RowNames”, S.LastName);

登录评论。


Econstudent"class=
Econstudent 2017年1月17日
你详细讨论了为什么我们不应该A, B或C,你还评论了我们如何访问某些对象。
现在,假设我们需要导入几个时间序列——但我一次只能导入一个。在循环中创建变量序列的目的通常是每次将这些时间序列存储在不同的对象中。也就是说,您希望每次都将数据分配给不同的对象,并且要多次这样做……
除了在循环中创建对象,还有什么其他选择?
7评论
斯蒂芬。"class=
斯蒂芬。 2021年8月10日
"我仍然想知道我使用eval是否仍然被弃用"
使用LOAD导入文本文件数据很久以前就被弃用了,取而代之的是专门为文本文件设计的更通用的函数:

登录评论。


扫罗卡马乔"class=
扫罗卡马乔 2021年3月10日
我如何得到工作空间部分的变量?
1评论
亚当Danz"class=
亚当Danz 2021年3月10日
这个问题问得不对。问题是这些变量最初是如何在(基本?)工作空间中结束的,以及如何更改该方法,以便将它们加载到函数空间或作为输入传递。

登录评论。