40

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

这真的是最好的选择吗?

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

4

8 回答 8

35

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 微秒,请参阅这个问题)。如果您在循环中使用常量,请在进入循环之前将其复制到局部变量中。如果由于某种原因您必须使用对常量的直接访问,请使用返回值的普通函数。

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

于 2009-11-23T17:06:42.407 回答
21

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

否则,您可以使用 MATLAB 类来定义命名常量。

于 2009-11-20T23:31:40.430 回答
8

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

于 2010-10-21T14:09:04.393 回答
4

您可能会回答其中一些问题如何在 MATLAB 中创建枚举类型?有用。但简而言之,没有一种“单行”方式来指定在 MATLAB 中进行初始设置后其值不应改变的变量。

于 2009-11-20T23:28:48.873 回答
2

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

于 2009-11-20T23:56:17.027 回答
1

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

% 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();
于 2014-11-26T20:07:50.820 回答
0

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

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 表和一个 matlab 函数,该函数返回具有这些列出值的结构:Matlab FileExchange

于 2012-02-24T17:57:07.580 回答
0

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

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

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

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

于 2018-02-08T13:04:13.480 回答