为什么`eval`比`str2func`更差来评估字符串中的函数?

时间:2017-09-14 07:54:22

标签: matlab eval

我已经展示了performance of str2func is better,但我收到了很多评论,说明有更多基础reasons不使用eval。哪些基本原因适用于eval,并且在以下情况下不适用于str2func

f='a^x+exp(b)+sin(c*x)+d'
  1. eval

    y = eval(f)
    

    或(由rahnema1建议)

    fHandle = eval(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    
  2. str2func

    fHandle = str2func(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    
  3. 为什么第一个选项比第二个选项更差,除了performance reasons

    说明

    • 请注意,我知道如果可能的话,最好避免使用这两种方法。

    • 请注意,我将eval的输出分配给变量,这样可以避免执行大量棘手的代码。

4 个答案:

答案 0 :(得分:10)

不要试图淡化eval的安全风险。如果您不完全控制输入,则运行任意代码可能会有害。如果您完全控制输入,几乎总是是一种不涉及动态代码生成的更好方法。

你坚持一个具体的例子:

>> ls TMPFILE
Error using ls (line 35)
ls: cannot access 'TMPFILE': No such file or directory

>> y = eval('system(''touch TMPFILE'')');
>> y

y =

     0

>> ls TMPFILE
TMPFILE

touch是一个非常友好的unix命令;它会创建一个空文件。想象一下rm -rf ~在同一个环境中。

让我们试试str2func

>> y = str2func('system(''touch TMPFILE2'')');
Warning: The input to STR2FUNC "system('touch TMPFILE2')" is not a valid function name. This will generate an error in a future release. 
>> ls TMPFILE2
Error using ls (line 35)
ls: cannot access 'TMPFILE2': No such file or directory

>> y

y =

  function_handle with value:

    @system('touch TMPFILE2')

>> y()
Undefined function or variable 'system('touch TMPFILE2')'.

旁注:虽然eval的风险是真实的,而不仅仅是因为迷信,使用eval并不一定意味着您是安全的。我最喜欢的例子是str2num的偷偷摸摸的恶魔:

>> x = str2num('[system(''touch TMPFILE3'')]')        

x =

     0

>> ls TMPFILE3
TMPFILE3

因此,即使您没有明确使用eval,也可能会使用您正在使用的便利功能 。您应始终确保不会发生这种情况:使用保险箱str2double代替str2num,并使用str2func代替eval(因为我们在上面看到str2func 1}} 不会执行任意代码。)

答案 1 :(得分:9)

首先,性能(特别是x100较慢)应该是不使用某些东西的充分理由。

然而,你错过了这一点。您现在问“为什么不在评估字符串中函数的具体示例中使用eval?”。那么,答案就是因为你有一个名为str2func的函数来专门更快,更安全地完成这项工作。你不应该使用eval的原因是因为在你想要使用eval的情况下,代码的逻辑是有缺陷的。

eval的唯一原因是评估一个任意输入不仅一个字符串上的函数(为什么你会这样做,你已经证明有一个特定的函数为了它)。如果你知道你在评估什么,那么你不需要eval,你可以编写你期望的代码。因此,eval仅在您接受通用输入时使用。但是通用输入包括rm -rf以删除整个操作系统。或者在一个不那么灾难性的情况下,代码可能会重写一个对算法其余部分很重要的变量。很明显,为什么你不想让代码运行任意输入。

动态变量怎么样?可以使用eval生成的A terrible idea。你可能会通过接受任意输入而意外地做到这一点。

但还有更多的事情。 eval确实使您的代码无法读取。在运行时之前,您根本不知道代码的作用。

我见过在其核心功能

中执行此操作的代码
model.solve_algorithm=eval(['default(',[ class(input3) ],')']);
result=eval(model.solve_algorithm);

代码是做什么的?有什么选择?没有办法知道,除非你运行它,看看它去了哪里。这使代码变得模糊,难以阅读,并且当然难以维护。在代码中明确表示在代码的可维护性方面确实受益匪浅。

TLDR:在任何情况下,您可能想要使用eval,其中一个正在发生:

  1. 对于您想要做的事情,有一个更好,更具体的功能
  2. 你真的不应该做你想做的事情,代码/逻辑需要重组。

答案 2 :(得分:8)

TL; DR - eval可以访问本地定义的函数,而str2func则不能。 "权利"在这两者中使用的函数取决于代码的结构。

没有进入整体"为什么eval是坏的"讨论,我将集中讨论在你的问题的背景下讨论它的MATLAB documentation的相关部分:

考虑一个包含两个函数的文件:

function out = q46213509
  f='a^x+exp(b)+sin(c*x)+d';
  fHandle = {eval(['@(x, a, b, c, d) ' f]), str2func(['@(x, a, b, c, d) ' f])};
  out = [fHandle{1}(1,2,3,4,5) fHandle{2}(1,2,3,4,5)];
end

function e = exp(x)
  e = x.^0./factorial(0) - x.^1./factorial(1) + x.^2./factorial(2); % Error is deliberate
end

运行上述内容时,会产生:

ans =

    8.7432   26.3287

如果您正在使用课程并且定义your own operators,这可能会失控......让我们说某人决定将文件添加到您的MATLAB path,并且方便地给它一些你使用的函数的名称,或者一个可重载运算符的名称(即mpower.m):

function out = mpower(varargin)
  % This function disregards all inputs and prints info about the calling workspace.
  disp(struct2cell(evalin('caller','whos')));
end

虽然在某些情况下,MATLAB不会重新定义内置函数,但我敢打赌,上面的场景可能会让str2func混淆不清......

答案 3 :(得分:8)

我暂时搁置了反对这两种方法的论据,尽管它们都非常有效,并且尽责的读者应该尝试理解它们。

我可以看到两个明显的差异。对于这些示例,我们必须假设您已经使用一些可变数据构建了脚本中函数的输入字符串,并假设它可能是任何(错误或其他方式)。因此,我们准备并解决最坏的情况。

  1. str2func包含一些额外检查,以确保您传递的内容是有效的功能,这可能会避免不必要的行为。我们不要采取"是的立场,但你可以这样做,以避免这种情况。看一个例子......

    % Not assigning an output variable
    % STR2FUNC: You've harmlessly assigned ans to some junk function
    str2func('delete test.txt') 
    % EVAL: You've just deleted your super important document
    eval('delete test.txt')     
    
    % Assigning an output variable
    % STR2FUNC: You get a clear warning that this is not a valid function
    f = str2func('delete test.txt') 
    % EVAL: You can a non-descript error "Unexpected MATLAB expression"
    f = eval('delete test.txt')
    
  2. 另一个差异是大约一半str2func documentation的主题。它涉及变量范围,位于标题"检查str2funceval"之间的差异。

      

    [函数简介]   
      如果使用匿名函数的字符向量表示,生成的函数句柄无权访问私有或本地函数。   

       [检查str2funceval] 之间的差异   
      当str2func与表示匿名函数的字符向量一起使用时,它无权访问本地函数[...]。 eval函数可以访问本地函数。

  3. 总而言之,你可能有一些用例,其中每个函数都是可取的,具体取决于你想要的错误处理和变量作用域绝不应该尽可能使用evalstr2func,如其他答案中所述。