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

Why is `eval` worse than `str2func` to evaluate a function from a string?

我已经证明 , but I got a lot of comments stating that there are more fundamental reasons to not use eval. Which fundamental reasons do apply to eval and do not apply to 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);
    

除了,为什么第一个选项比第二个选项差?

备注

停止试图淡化 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 不会执行任意代码)。

首先,性能(尤其是慢 100 倍)应该足以成为不使用某些东西的理由。

但是,您没有抓住重点。您现在问的是 "why not use eval in the specific example of evaluating a function in a string?"。好吧,答案是因为你有一个名为 str2func 的函数来专门更快更安全地完成这项工作。你不应该使用 eval 的原因是因为在你想使用 eval 的情况下,你的代码逻辑是有缺陷的。

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

动态变量呢? 可以使用 eval 生成。您可能会通过接受任意输入而意外地做到这一点。

但是还有更多的东西。 eval 确实使您的代码不可读。在 运行time.

之前,您不知道代码的作用

我在其核心功能中看到了执行此操作的代码

model.solve_algorithm=eval(['default(',[ class(input3) ],')']);
result=eval(model.solve_algorithm);

代码的作用是什么?有什么选择?没有办法知道,除非你 运行 它,看看它去了哪里。这使得代码变得混乱且难以阅读,当然也难以维护。在代码中显式确实对代码的可维护性有很大好处。

TLDR: 在任何情况下,您可能想要使用 eval,两者之一正在发生:

  1. 有更好、更具体的函数可以满足您的需求
  2. 你真的不应该做你想做的事,code/logic需要重组。

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


在不进入整个 "why eval is bad" 讨论的情况下,我将尽量专注于 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 this might get out of hand... Let's say somebody decided to add a file to your 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 混淆很多...

我暂时搁置反对这两种方法的论点,尽管它们都是 非常 有效的,认真的 reader 应该尝试理解它们。


我可以看到两个明显的区别。对于这些示例,我们必须假设您已经使用一些变量数据为脚本中的函数构建了输入字符串,并假设它可能是 anything(无论是错误的还是其他原因)。所以我们准备并考虑最坏的情况。

  1. str2func 包括 一些 额外检查以确保您传递的是有效函数,这可能会避免不必要的行为。先不站在"yeah but you could do it this way to avoid that"的立场,看个例子...

    % 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。它涉及变量范围,可在 "Examine Differences Between str2func and eval".

    标题下找到

    [Intro to the function]
    If you use a character vector representation of an anonymous function, the resulting function handle does not have access to private or local functions.

    [Examine Differences Between str2func and eval]
    When str2func is used with a character vector representing an anonymous function, it does not have access to the local function [...]. The eval function does have access to the local function.


总而言之,您 可能有这样的用例,其中每个函数都更可取,具体取决于您所需的错误处理和变量范围 永远不应使用 evalstr2func 如其他答案所述,只要有可能。