【问题标题】:Constants in MATLABMATLAB 中的常量
【发布时间】:2010-12-18 22:24:03
【问题描述】:

我已经拥有了一堆 MATLAB 代码,并注意到代码中散布着一堆“神奇的数字”。通常,我喜欢用 C、Ruby、PHP 等语言制作这些常量。当谷歌搜索这个问题时,我发现拥有常量的“官方”方式是定义返回常量值的函数。看起来很笨拙,特别是因为当每个文件允许多个函数时,MATLAB 可能会很挑剔。

这真的是最好的选择吗?

我很想使用/制作类似 C 预处理器的东西来为我做这件事。 (我发现一个名为mpp 的东西是由处于类似困境的其他人制作的,但它看起来已被废弃。代码无法编译,我不确定它是否能满足我的需求。)

【问题讨论】:

    标签: matlab constants matlab-class


    【解决方案1】:

    Matlab 现在有常量了。 Matlab OOP 较新的 (R2008a+) "classdef" 样式允许您定义常量类属性。如果您不需要向后兼容旧的 Matlab,这可能是最好的选择。 (或者,相反,这是放弃向后兼容性的一个很好的理由。)

    在一个类中定义它们。

    classdef MyConstants
        properties (Constant = true)
            SECONDS_PER_HOUR = 60*60;
            DISTANCE_TO_MOON_KM = 384403;
        end
    end
    

    然后使用点限定从任何其他代码中引用它们。

    >> disp(MyConstants.SECONDS_PER_HOUR)
            3600
    

    有关详细信息,请参阅“用户指南”下的“面向对象编程”的 Matlab 文档。

    有几个小问题。如果代码不小心尝试写入常量,而不是得到错误,它将创建一个本地结构来屏蔽常量类。

    >> MyConstants.SECONDS_PER_HOUR
    ans =
            3600
    >> MyConstants.SECONDS_PER_HOUR = 42
    MyConstants = 
        SECONDS_PER_HOUR: 42
    >> whos
      Name             Size            Bytes  Class     Attributes
    
      MyConstants      1x1               132  struct              
      ans              1x1                 8  double              
    

    但损害是局部的。如果你想彻底,你可以通过在函数开头调用 MyConstants() 构造函数来防止它,这会强制 Matlab 将其解析为该范围内的类名。 (恕我直言,这是矫枉过正,但如果你想要它就在那里。)

    function broken_constant_use
    MyConstants(); % "import" to protect assignment
    MyConstants.SECONDS_PER_HOUR = 42 % this bug is a syntax error now
    

    另一个问题是 classdef 属性和方法,尤其是像这样的静态,很慢。在我的机器上,读取这个常量比调用普通函数慢大约 100 倍(22 微秒与 0.2 微秒,参见this question)。如果您在循环中使用常量,请在进入循环之前将其复制到局部变量中。如果由于某种原因您必须直接访问常量,请使用返回值的普通函数。

    为了您的理智,请远离预处理器。让它在 Matlab IDE 和调试器(非常有用)中工作需要深入而可怕的 hack。

    【讨论】:

    • 喜欢使用在较新版本的 Matlab 中引入的 OOP 东西,但它在我们的测试中使事情慢得令人沮丧。 (当从嵌套函数更改为 OOP 内容时,运行 simple 函数 100,000 次从耗时 0.0837 秒变为 2.3689 秒。)我们编写的大部分内容都需要进行相当优化,因此开销相当大威慑。
    • 我听到了。 (参见关于 OOP 性能的其他链接 Q。)出于同样的原因,我很少使用它。您可以混合搭配:将常量放在 classdef 中以进行组织,并将其余代码保留在函数或旧式(更快)OOP 中。开销是每次调用;引入 OOP 不会使现有的非 OOP 代码变慢。在进入紧密循环之前将常量拉到局部变量中,这可能没问题。除此之外,返回常量值的函数是 2008 年之前惯用的做常量的方法,也是我的建议。
    • “深刻而可怕的黑客攻击”——我在处理软件时听到的最不祥的短语之一。我认为自己受到警告!
    • 相信 Matlab 会采用几乎所有编程语言的简单、固有部分并将其彻底搞砸。 Matlab 越早死于痛苦的死亡,恕我直言越好。
    【解决方案2】:

    我通常只是用 UPPER_CASE 定义一个变量并放在文件顶部附近。但你必须承担不改变其价值的责任。

    否则你可以use MATLAB classes定义命名常量。

    【讨论】:

    • 我通常使用 MATLAB 类来保存我的所有可配置参数。这也使您能够创建多个配置并轻松地将它们换入和换出。您甚至可以创建一个配置数组并遍历该数组,依次在每个配置上运行您的测试代码。
    【解决方案3】:

    MATLAB 没有精确的 const 等效项。我建议不要对常量使用全局 - 一方面,您需要确保在您想使用它们的任何地方声明它们。我会创建一个函数来返回你想要的值。您可以查看this blog post 了解一些想法。

    【讨论】:

      【解决方案4】:

      这些答案中的一些How do I create enumerated types in MATLAB? 可能有用。但简而言之,没有一种“单行”方式来指定在 MATLAB 中进行初始设置后其值不应改变的变量。

      【讨论】:

        【解决方案5】:

        不管你怎么做,它仍然会有点杂乱无章。在过去的项目中,我的处理方法是将所有常量定义为一个脚本文件中的全局变量,在程序执行开始时调用脚本来初始化变量,并包含“global MYCONST;”。需要使用 MYCONST 的任何函数开头的语句。这种方法是否优于定义函数以返回常量值的“官方”方法是一个意见问题,无论哪种方式都可以争论。两种方式都不理想。

        【讨论】:

          【解决方案6】:

          我处理要传递给其他函数的常量的方法是使用结构:

          % Define constants
          params.PI = 3.1416;
          params.SQRT2 = 1.414;
          
          % Call a function which needs one or more of the constants
          myFunction( params ); 
          

          它不像 C 头文件那样干净,但它完成了这项工作并避免了 MATLAB 全局变量。如果您希望将常量全部定义在一个单独的文件中(例如 getConstants.m),那也很容易:

          params = getConstants();
          

          【讨论】:

            【解决方案7】:

            不要在没有先创建实例的情况下使用myClass.myconst 调用常量!除非速度不是问题。我的印象是,第一次调用常量属性会创建一个实例,然后所有未来的调用都会引用该实例 (Properties with Constant Values),但我不再相信这种情况。我创建了一个非常基本的表单测试函数:

            tic;
            for n = 1:N
                a = myObj.field;
            end
            t = toc;
            

            类定义如下:

            classdef TestObj
                properties
                    field = 10;
                end
            end
            

            或:

            classdef TestHandleObj < handle
                properties
                    field = 10;
                end
            end
            

            或:

            classdef TestConstant
                properties (Constant)
                    field = 10;
                end
            end
            

            针对不同情况的对象、句柄对象、嵌套对象等(以及赋值操作)。请注意,这些都是标量;我没有研究数组、单元格或字符。对于 N = 1,000,000,我的结果(总经过时间)是:

            Access(s)  Assign(s)  Type of object/call
              0.0034    0.0042    'myObj.field' 
              0.0033    0.0042    'myStruct.field'  
              0.0034    0.0033    'myVar'                   //Plain old workspace evaluation
              0.0033    0.0042    'myNestedObj.obj.field'   
              0.1581    0.3066    'myHandleObj.field'   
              0.1694    0.3124    'myNestedHandleObj.handleObj.field'   
             29.2161         -    'TestConstant.const'      //Call directly to class(supposed to be faster)
              0.0034         -    'myTestConstant.const'    //Create an instance of TestConstant
              0.0051    0.0078    'TestObj > methods'       //This calls get and set methods that loop internally
              0.1574    0.3053    'TestHandleObj > methods' //get and set methods (internal loop)
            

            我还创建了一个 Java 类并运行了类似的测试:

             12.18     17.53      'jObj.field > in matlab for loop'
              0.0043    0.0039    'jObj.get and jObj.set loop N times internally'
            

            调用 Java 对象的开销很高,但在对象内,简单的访问和分配操作与常规 matlab 对象一样快。如果您希望引用行为启动,Java 可能是要走的路。我没有调查嵌套函数中的对象调用,但我看到了一些奇怪的事情。此外,当涉及到很多这些东西时,分析器是垃圾,这就是为什么我改用手动保存时间。

            供参考,使用的Java类:

            public class JtestObj {
                public double field = 10;
            
                public double getMe() {
                    double N = 1000000;
                    double val = 0;
                    for (int i = 1; i < N; i++) {
                        val = this.field;
                    }
            
                    return val;
                 }
            
                 public void setMe(double val) {
                    double N = 1000000;
                    for (int i = 1; i < N; i++){
                        this.field = val;
                    }
                 }
              }
            

            在相关说明中,这里有一个指向 NIST 常量表的链接:ascii table 和一个返回具有这些列出值的结构的 matlab 函数:Matlab FileExchange

            【讨论】:

            【解决方案8】:

            我使用带有简单常量大写字母的脚本,并在其他脚本中包含该脚本 tr=它们。

            LEFT  = 1;
            DOWN  = 2;
            RIGHT = 3; etc.
            

            我不介意这些不是恒定的。如果我写“LEFT=3”,那么我肯定是愚蠢的,无论如何也无法治愈愚蠢,所以我不打扰。 但我真的很讨厌这种方法使我的工作空间变得杂乱无章,而这些变量我永远不必检查。而且我也不喜欢使用诸如“turn(MyConstants.LEFT)”之类的东西,因为这会使语句变得更长,比如无数个字符宽,使我的代码不可读。

            我需要的不是变量,而是拥有真正的预编译器常量的可能性。也就是说:在执行代码之前被值替换的字符串。应该是这样的。常量不应该是变量。它只是为了使您的代码更具可读性和可维护性。 MathWorks:请,请,请。实现这一点并不难。 . .

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-04-24
              • 1970-01-01
              • 2012-03-17
              • 2018-12-06
              • 1970-01-01
              • 1970-01-01
              • 2012-02-19
              • 1970-01-01
              相关资源
              最近更新 更多