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

3903次观看(过去30天)
Stephen23"class=
Stephen23 2016年9月26日
编辑: Stephen23 2022年5月31日
简介:
动态访问变量名可能会对代码的可读性产生负面影响,并且可能会阻止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种常用选项]
要了解更多信息,请参见本页其余部分的更长的讨论。”
我知道我可以自己做,但您已经花了很多精力创建和更新这个页面。

登录发表评论。

接受的答案

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

登录发表评论。

更多的答案(19)

Stephen23"class=
Stephen23 2016年9月26日
编辑:Stephen23 2022年2月15日
MATLAB的文档 eval函数的替代 解释使用的代码 eval 是慢的,因为 “MATLAB®编译代码的第一次运行,以提高性能为未来的运行。然而,由于eval语句中的代码在运行时可能会更改,所以它不会被编译。
MATLAB使用 JIT 加速工具,在执行代码时分析代码,并优化代码以更有效地运行。当 eval JIT优化是无效的,因为每个字符串都必须在每次迭代中重新编译和运行。这与 eval 非常缓慢。这也是为什么不只是 创建 带有动态变量名的变量比较慢,但是 访问 他们也很慢。
即使是 eval 隐藏在 str2num 可以减慢代码:

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

登录发表评论。


Stephen23"class=
Stephen23 2016年9月26日
编辑:Stephen23 2022年2月15日
难以共事
很多初学者来这里的问题基本上都是 “我有很多编号的变量,但我不知道如何做这个简单的操作……” ,或称“ 我的代码非常慢/复杂/有bug…我怎样才能使它变得更好呢?”
甚至的拥护者 eval 你会被它弄糊涂,不能让它正常工作,甚至不知道为什么,就像下面两个例子清楚地显示的那样:
为什么他们弄不明白为什么它不起作用?:
  • 完全混淆代码。间接评价代码。
  • 代码辅助工具不工作。
  • 语法高亮显示不起作用。
  • 静态代码检查不起作用。
  • 没有有用的错误消息,等等。
编写代码是困难的。不要关闭检查和改进代码的工具,这会让工作变得更加困难。

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

Stephen23"class=
Stephen23 2016年9月26日
编辑:Stephen23 2021年11月7日
混淆代码的意图
什么 这段代码 做什么?:
x1 =[119101、98、39104116116112,58岁,47岁,47119119119年);
x2 = (46121111117116117, 98101, 99111109, 47119, 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,也更难调试!看到这些例子:
正确编写的代码是清晰和可理解的。清晰易懂的代码更容易编写、修复和维护。阅读代码的次数比编写代码的次数要多,所以永远不要低估编写代码的重要性:编写代码注释,编写帮助部分,使用一致的格式和缩进,等等。
4评论
Stephen23"class=
Stephen23 2021年11月7日
编辑:Stephen23 2021年11月7日
@Walter罗伯森 :这个例子应该是STR2NUM吗?(其中包含EVAL)

登录发表评论。


Stephen23"class=
Stephen23 2016年9月26日
编辑:Stephen23 2021年11月7日
代码助手工具不工作
MATLAB编辑器 包含许多高级用户的工具 不断 的使用,初学者在学习MATLAB时应该特别欣赏。然而 没有一个 这些工具中的大多数都与使用 eval
注意,这些 不工作 当使用 eval evalc 等等,神奇地创建或访问变量名 .您想要禁用帮助您编写功能代码的工具吗?下面是一些例子 eval 隐藏代码错误,使调试代码变得困难:
1评论
汤姆·霍金斯"class=
汤姆·霍金斯 2019年2月7日
在这个主题上,如果代码分析器和 checkcode 什么时候会发出警告呢 eval 使用等。也许这样就能减少关于他们的问题了?

登录发表评论。


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


Stephen23"class=
Stephen23 2016年9月26日
编辑:Stephen23 2022年2月10日
选择: 负载 进入结构,而不是工作区
MATLAB文档详细解释了这一点:
在几乎所有以编程方式导入数据的情况下(例如,不只是在命令窗口中玩弄),建议这样做 负载 数据转换为输出参数(即 结构 如果文件是 .mat 文件):
S =负载(...);
结构的字段可以直接访问,例如:
S.X
S.Y
或使用 动态字段名 .注意,这是相反的 保存标量结构的字段
重要的是要注意(与许多初学者似乎认为的相反),实际上在循环中更容易 保存 而且 负载 中的变量名时的数据 .mat 文件 不改变 ,因为必须处理每个文件中不同的变量名,实际上增加了保存/加载文件的难度。
总结:在使用循环时,保持所有变量名相同!
以下是加载到变量的真实例子:
最后是Steven Lord的评论 负载 -直接进入工作区:

Stephen23"class=
Stephen23 2016年9月26日
编辑:Stephen23 2021年11月7日
备选方案:非标量结构(带索引)
使用一个 种基本结构 比尝试访问动态变量名要简单得多。下面是一些例子:
一个非常简单的例子是使用DIR的输出来存储导入的文件数据:
S = dir (. .);
k = 1:元素个数(S)
(k)。数据= readmatrix(S(k).name);
结束

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

Stephen23"class=
Stephen23 2017年7月19日
编辑:Stephen23 2022年5月31日
神奇地让变量出现在工作区中是有风险的
这导致了许多微妙的bug,即使它们被注意到,也很难追踪到!
1)对于一个启动变量,同名变量将被覆盖而没有警告。即使只是一个拼写错误或向MAT文件添加额外的变量也会改变代码的行为,而且由于这取决于您正在处理的数据文件,因此很难跟踪到。
2)循环导入多个文件 毁了你的数据 :考虑一下,如果您的代码处理MAT文件序列,将会发生什么 认为 它们都包含相同的变量。但其中一个包含不同的变量(是的,我知道,你的数据文件是完美的…确定)。考虑一下在编写糟糕、脆弱的代码中直接加载到工作区中会发生什么:它将愉快地处理来自先前加载文件的数据,而不会向您发出任何关于数据现在完全错误的警告或通知。处理过程继续使用错误的数据。
3)还有一个严重而微妙的问题,这是由MATLAB解析器寻找替代函数/对象/…调用那些而不是使用神奇创建的变量:基本上if变量 不存在 然后解析器 它最好的 为了找到与这个名字后来被调用/使用的地方相匹配的东西……它可能会找到一些东西!例如:
或者在某些情况下,解析器可能会 找到任何:
解决方法很简单:不要神奇地“噗”地一声将变量创造出来:总是 负载 到结构中,永远不要动态地创建变量名。
6个评论
沃尔特·罗伯森"class=
沃尔特·罗伯森 2019年8月21日
目前,每当MATLAB路径发生变化时,就会进行一些名称重新解析,包括当您使用cd()时——这是避免在代码中使用cd()的一个原因。
在过去,为了有效地处理这种情况,我考虑了一些你必须建立的结构。不过,我并没有坚持到底;只是一些思想实验。

登录发表评论。


Stephen23"class=
Stephen23 2016年9月26日
编辑:Stephen23 2021年11月7日
混淆数据和代码
在变量名中包含数据和元数据(例如,用用户的输入命名变量、测试主题的名称,或者(非常常见的)在变量名上添加索引)是一个微妙的(但密切相关的)问题,应该绝对避免。
在变量名中包含元数据的问题是,这打破了代码是 广义 ,因为它混合了 代码 而且 数据 在一起。事实上,数据和代码应该分开,以保持代码的泛化。编写成as的代码 一般尽可能 更简单、更健壮、更可适应、更容易编写、更容易修复,从而使代码少了很多bug。混合 元- 将数据转换为变量名只会使一切变得更加复杂,这反过来又会使代码变得缓慢和有bug。
阅读这些讨论,了解为什么把数据和元数据放在变量名中是一种糟糕的做法:
在许多情况下,元数据只是一个 实际 索引,即禁止数据顺序的值。但在这种情况下 实际 索引应该变成一个更有效的 真正的 数值指数:
2的评论
沃尔特·罗伯森"class=
沃尔特·罗伯森 2022年1月26日
MATLAB支持目标系统 哈佛体系结构 ——指令和数据位于不同地址空间的系统,因此指令不是这些系统上的数据。

登录发表评论。


Stephen23"class=
Stephen23 2016年9月26日
编辑:Stephen23 2021年11月7日
可选:使用更有效的方法在工作区之间传递变量(适用于 evalin assignin 等)
使用 嵌套函数 ,或传递参数,或使用任何其他有效的方式在工作区之间传递数据:

Stephen23"class=
Stephen23 2017年11月30日
编辑:Stephen23 2021年11月7日
PS: eval 不是错误的:
一些初学者显然是这么想的 eval (和朋友)必须是错误的,应该从MATLAB中删除。他们会问“如果 eval 都这么坏了,为什么还不把它拔掉呢?”但是重要的是要理解这个问题是由神奇地访问变量名引起的 无论使用什么工具或操作 ,这 eval (或 assignin ,或 evalin ,或 负载 没有输出参数等)只是使用不当,因为有很多 更好的 方法( 更好的 即更快、更整洁、更简单、更健壮等)。阅读下面的讨论,找到关于这种困惑的好例子:
注意到这一点很重要 任何 语言的特性可以被低效地或不恰当地使用,而不是仅仅使用 eval ,这是 可以由语言本身控制的东西。例如,有些人可能会解决一些带有慢循环和没有慢循环的问题 预分配输出数组 :这是 意味着 循环是“错误的”,需要从MATLAB中删除!
编写有效的代码取决于程序员。

Stephen23"class=
Stephen23 2019年4月17日
编辑:Stephen23 2019年4月17日
选择: 保存 标量结构的字段
保存 命令有一个选项,可将标量结构的字段保存为控件中的单独变量 .mat 文件。例如,给定一个标量结构:
s.a. = 1;
mit获得=(2、3);
这将在.mat文件中保存变量A和B:
保存(“myfile.mat”“结构”“年代”
这是的反函数 加载到结构中 .一些线程展示了如何使用这个:

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

登录发表评论。


Econstudent"class=
Econstudent 2017年1月17日
你详细地讨论了为什么我们不应该A、B或C,你还评论了我们如何访问某些对象。
现在,假设我们需要导入几个时间序列——但我一次只能导入一个序列。在循环中创建变量序列的目的通常是每次将这些时间序列存储在不同的对象中。也就是说,您希望每次都将数据分配给不同的对象,并多次执行此操作……
除了在循环中创建对象之外,还有什么其他选择?
18岁的评论
塞缪尔·格雷"class=
塞缪尔·格雷 2022年2月20日
编辑:塞缪尔·格雷 2022年2月20日
“但首先你需要看看任何针对多个操作系统的重要C程序,看看头文件中大量的#ifdef。”
我认为这只是一个问题,因为我们不确切地知道哪些#定义需要包含在程序中,它们需要有什么值,以及从哪里获取它们。
所以在C/ c++中缺乏标准化。你总是可以用像Python这样的“高级”语言编写程序,让Python的开发人员来处理那些“C级”的问题。我相信微软会帮助你,如果你只是采用VS 2019+作为你的IDE,没有问题。
我个人同意,C语言是最令人沮丧的编程语言之一,原因很简单。c++就像在吃了迷幻药后尝试用C语言编程。让我们把代码扔到一个满是特定于平台的头文件的库中,让它变得更困难。但部分原因是微软就是微软,它走进了玩游戏的一部分人,改变了规则,改变了所有的部分,称它是同一个游戏。只有当它们被控制时,它们才与操作系统相关。这就是为什么整个x86市场都拒绝接近Windows的原因。
但是BSD人群也要为此负责,因为他们没有坚持自己的开发指南,制作makefile为即使是最绿色的C新手详细说明如何制作和安装他们的程序。当然,这也取决于具体的构建环境。微软非常擅长的一件事是抓住操作系统而不犯操作系统风格的错误。但是他们经常犯ms风格的错误,这也是我们仍然使用Matlab的原因之一。如果没有Matlab,就会有一群人用不同的语言编程,几乎没有共同点。现在在Linux上使用Matlab的好处是希望你们能放弃COM。Matlab的特性依赖于COM,同样也依赖于Windows。其中大部分都可以在VBA中轻松编写。COM只是一种绝望的尝试,想要复制Unix中存在了几十年的功能,现在Canonical已经和MS合作,把Ubuntu和Windows捆绑在一起了,你为什么还需要COM呢?你可以把Matlab和Ubuntu捆绑在一起。
你只需要弄清楚如何为Matlab收费;)
(这就是工具包的作用!)
...最终,BSD人群是精英,他们并不真正关心某人是否发现在他们个人喜欢的Unix风格中开发很困难。这对他们来说只是意味着更多的工作和更多的收入。你想要一个易于开发的东西,Python总是有的,对吧?在您发现Python也依赖于库文件版本之前,用Python编程是一个好主意。和认证代码?哈哈

登录发表评论。


约翰Dzielski"class=
约翰Dzielski 2022年2月19日
我有一个关于eval命令的特定用例的问题。它是我经常使用的,我想知道它是否和为什么是坏的。I数据集来自一段工具,其中文件名或变量名通常包含一些标识字符串和某种编号。当我编写分析脚本时,我通常会假设数据存储在一个名为“data”的变量中。我将使用eval(['data=',namedVariable])这样的命令将值赋给'data',然后运行脚本。我经常使用参数的相反名称,将处理过的数据复制回相同的变量,并将其保存到文件中。这些脚本通常是LiveScripts,而情节标题通常来自'namedVariable',因此函数调用在这里不是一个有用的解决方案。这样做有什么错?(如果有的话)。
14日的评论
里克"class=
里克 2022年2月20日
不知道避免eval需要的特定语法并不是什么羞耻的事情。你的错误是重复了“我不认为有办法在这里避免评估”。正如我们已经多次演示的那样,通常是这样的。
请将下一个问题作为一个单独的问题发布,这样其他人也可以找到解决方法。(不要认为唯一的方法是eval)

登录发表评论。


Baidu
map