【问题标题】:How to determine if a function was called followed by a semicolon (";")?如何确定是否调用了函数后跟分号(“;”)?
【发布时间】:2015-01-18 20:53:15
【问题描述】:

在一个 Matlab 脚本中,我调用了一个用户定义的函数(m-function)。我的函数在使用disp 和/或fprintf 调用将值打印到命令窗口时返回一个值。

在编写表达式或语句时,将; 放在其末尾以禁止打印。当表达式调用我的函数时,; 可以禁止打印返回值。但是,这不会影响被调用函数内的disp 输出。

我想在适当的时候消除函数的显示输出。 有没有办法确定函数调用是否在以; 结尾的表达式中进行

【问题讨论】:

  • 我找到了部分解决方法 - 使用 evalc 函数调用我的函数。我稍后会详细说明。这种方法仍然没有回答问题。
  • 你的函数在做什么内部输出?这只是为了实现返回值的显示吗?还是更像是日志记录或调试?你能给我们举个具体的例子吗?特别是,正在显示的内容不包含在函数返回的值中?
  • 我认为向函数添加一个额外的输入参数来确定显示的内容是迄今为止最简单的选项,或者将所有显示输出保存为另一个函数输出,以便在运行函数后访问它如果你需要的话。
  • @AndrewJanke - 在这种特殊情况下,它只是返回值的格式化输出。但是,这对于问题的本质并不重要。
  • @David - 保存显示输出基本上是evalc() 正在做的事情。是的,一个额外的论点是最简单的,只要没有直接的方式来说明(在概念上类似于nargin/nargout)。

标签: matlab function expression


【解决方案1】:

我喜欢您尝试做的事情的精神,但我认为这可能与 Matlab 中的常见编程模式背道而驰。正如您正确指出的那样,终止分号的目的是禁止打印返回值。试图让它融入您的其他功能可能需要一些深度黑客攻击和难看的难以维护的代码。实现您所描述的标准方法是通过属性名称-值对参数。例如,Matlab 的优化套件有一个名为'Display' 的属性,可以将其设置为各种值以指示所需的详细程度(请参阅optimset)。

如果您想尝试寻找一种方法来检查终止分号,您可以查看未记录的 mintmlintmexmtree 函数 – read more here。不幸的是,使用 mlint 来简单地检查“用分号终止语句以抑制输出”警告 (see this function on the MatlabCental File Exchange) 在所有情况下都不起作用,因为函数调用本身不会产生此警告。

更新

这是一个可以插入到被调用函数中的代码尝试,以确定调用者的行是否由分号终止。您应该知道,这尚未经过彻底测试,并且可能非常脆弱。我没有使用子函数或匿名函数对其进行测试,我知道如果您使用 ... 将一行换成多行,它会失败。

st = dbstack('-completenames');  % M-file of caller and line number
caller = st(2);
str = mlint('-lex',caller.file); % Struct containing parsed data

isSemicolon = false; % Assume no semicolon
for i = 1:length(str)
    % Find end-of-line corresponding to function call
    c = strsplit(str(i).message,'/');
    if str2double(c{1}) == caller.line && strcmp(c{2}(end-5:end-1),'<EOL>')
        % Check for comments
        j = i-1;
        if ~isempty(strfind(str(j).message,'%'))
            j = j-1;
        end
        % Check for semicolon
        if strcmp(str(j).message(end-2),';')
            isSemicolon = true; % Semicolon found
            break;
        end
    end
end

换句话说,玩这个来学习,但我不建议实际使用它。

【讨论】:

  • 谢谢。首先,请参阅我对这个问题的评论。现在,我不一定要寻找 hack(严格来说),而是寻找一种标准的方法。因此,它不会违背编程模式。至于mlint 实现,通过快速浏览提供的链接,mlint 工具 m 文件上工作,解析其内容。我的问题是在运行时从 within 文件中执行此操作。关于财产的使用 - 我必须调查这个想法。
  • 您可以在运行时使用任何这些工具。对于比这更复杂的事情,我自己也这样做过。我的回答的重点是属性名称-值对模式是执行此操作的标准方式。使用evalc 或解析代码的方法将是非常不标准的黑客攻击,但它们可能是实际检测分号是否存在的唯一方法。
  • 如何从我的函数内部调用mlint,可以判断 caller 脚本在该调用行是否有分号?
  • 如果每次调用函数时都需要设置属性,那么我可以很容易地使用单个 argument 并在进入函数时检查它的存在/值。
  • @horchler:当你说“......但我建议实际使用它”时,你的意思是“......但我建议绝对不要实际使用它”吗?
【解决方案2】:

恐怕您的问题的答案是。该信息根本不会传递给被调用的函数。

您不应该考虑添加分号作为“禁止打印”的手段,而是缺少分号会指示 MATLAB 在函数调用的输出变量上调用 display 函数。换句话说,MATLAB 会解释这段代码:

y = myFunc(x)

作为:

y = myFunc(x);
display(y);

我认为在函数中添加“打印”或“详细”参数是实现所需目标的最佳选择。

【讨论】:

    【解决方案3】:

    我认为实现您想要的结果的最简单方法(即是否显示disp)是添加额外的函数输入或输出。例如,添加一个输入(可选,您可以设置默认行为):

    function y=myFunc(a,displayResults)
    if nargin==1
        displayResults=true; %// set the default behaviour
    end
    
    %// if you want to print something
    if displayResults
        disp(a)
    end
    end
    

    或者额外的输出。在这种情况下,foo 不会在屏幕上产生任何输出,但所有消息都保存到一个元胞数组中,如果需要,可以访问该数组:

    function [x,m] = foo(a)
    m={}; %// initialise cell array of output messages
    
    x=a;
    
    m{length(m)+1}=a; %// some message
    m{length(m)+1}='another message'; %// another message
    end
    

    我认为第一个选项会更好,第二个选项不能很好地处理fprintf,并且显示m 的元素可能会很棘手,具体取决于它包含的内容。第一种方法非常简单,甚至不需要您更改现有代码,因为您可以将displayResults 输入设为可选并将默认设置为您想要的。

    【讨论】:

      【解决方案4】:

      这是一个可能的解决方法(要明确 - 这本身并不是问题的真正答案,只是避免不必要行为的一种方法)。说我的功能是:

      function y = prt_test(x)
          y = x + 1;
          disp('IN PRT_TEST')
      end
      

      调用:

      >> % Regular use - message and output are displayed:
      >> y = prt_test(1)
      IN PRT_TEST
      y =
           2
      
      >> % Regular use w/ ";" - only message is displayed:
      >> y = prt_test(2);
      IN PRT_TEST
      
      >> % Use "evalc()" - message and output are displayed:
      >> evalc('y = prt_test(3)')
      ans =
      IN PRT_TEST
      y =
           4
      
      >> % Use "evalc()" w/ func ";" - only message is displayed:
      >> evalc('y = prt_test(4);')
      ans =
      IN PRT_TEST
      
      >> % Use "evalc();" - no output:
      >> evalc('y = prt_test(5)');
      
      >> % Use "evalc();" w/ func ";" - no output:
      >> evalc('y = prt_test(6);');
      
      >>
      

      【讨论】:

      • 我必须说这是用任何语言编写代码的糟糕方式。你也可能因为使用eval 而被否决。阅读thisthis。还有this
      • @horchler - downvoting w/o提出更好的方法比代码更能说明downvoter本人......我没有说它是干净的,但这是我能找到的唯一方法(w /o 明显的添加参数)。
      • @horchler - 真的,添加“打印”参数非常简单明了。但是它没有回答这个问题。问题不是寻求打印问题的解决方案,而是如何确定情况的答案。
      【解决方案5】:

      您可以通过在函数开头本地重新定义disp 输出来抑制它:

      function [] = hideDisplay()
      %[
          % Override `disp` behavior
          disp = @(x)doNothing;
      
          % Next `disp` calls will no longer appear in matlab console
          disp('Hello')
          disp('World!')
      %]
      end
      
      %% ---
      function [] = doNothing()
      %[
      %]
      end
      

      【讨论】:

      • 但是,如何从外部函数控制?
      • 一种解决方案是将伪造的disp.m 文件(什么都不做)放在您要控制的功能(或私人文件夹)的同一文件夹中。由于优先级偏好,Matlab 将调用此 disp.m 函数而不是通用函数。
      • 我认为您可能没有抓住重点 - 我想根据;控制调用脚本的输出。这意味着,在同一个脚本中,我可以进行不带打印的调用和不带打印的调用。
      • 嗯......确实不是很清楚......在我看来,你几乎用evalc解决了所有问题,期待disp调用......如果确实如此,可能解决方法是在您想要禁用显示时在调用函数之前创建假的“disp.m”......并在愿意正常显示时销毁此 disp.m 文件。无论如何,如果仍然没有得到它,祝你好运!
      • 注:我也不明白为什么要完全在调用函数之外执行此显示控制(除非这是您无法修改的代码)。
      猜你喜欢
      • 2019-07-15
      • 1970-01-01
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 2011-04-16
      • 2023-02-20
      • 1970-01-01
      相关资源
      最近更新 更多