关于编码风格

 

我提出这个话题,是因为我有一个很好的编码和重新编码的经验,很久以前用MQL4从头开始写,我想分享我的经验。

我的同事,我并不怀疑你快速编写实现算法的程序的能力。但我已经确定,如果你因为某种原因放弃了这个项目,一个月后回到这个项目,却不能立即理解它,你就不是一个好作家。在下面的内容中,我将告诉你我自己对编码风格的要求。对这些要求的遵守简化了进一步的修改。

0.不要急于马上行动,编写程序。按照经典的建议去做:花几个小时来思考程序的结构。然后你就可以坐下来快速、清晰、简洁地写一个程序。这几个小时将在写作和进一步调试的速度上得到许多倍的回报。

1.函数的长度不应明显超过20行。如果你不能实现它,那么有些地方你的逻辑和代码结构考虑得还不够好。此外,在最长的函数及其与它们所调用的函数的关系上,往往要花费最多的时间来进行代码调试。

例如,我的代码现在有629行,包含27个函数。这与函数调用结构的描述(2-6行)和每个函数前的简短评论,以及函数之间4-5行的空白分隔符一起。此外,我没有少放方括号(大括号),也就是说,每两个方括号我都分配了一行。如果我去掉函数前的所有注释,并减少函数之间的分隔符,那么27个函数将需要大约400行,也就是说,我的函数的平均长度大约是15行。

当然,这一规则也有例外--但这适用于最简单的函数或输出函数。一般来说,一个函数应该执行不超过3-5个不同功能的动作。否则,就会造成混乱。我通常也会在功能不同的动作之间加一个空行。

我有不少函数只有4行(这是一个最小值,函数主体有一行,每个声明行有一行,每个大括号有两行)到10行。我并不担心代码速度会因此而下降,因为实际上代码速度下降根本不是因为这个,而是因为手歪了。

2.不要吝惜解释动作、变量和函数的含义的评论。在这种情况下,对函数长度的20行限制保持不变。如果你破坏了它,就重写这个函数。你可以使用单行和多行注释。

3.我的职能是结构化的。有顶层(零层)调用的功能,有第一层、第二层等。下一级调用的每个函数都是由上一级的函数直接调用的。例如,像这样的一个函数。

// open
// .pairsToOpen
// .combineAndVerify( )
// Собирает из двух валют символ и выполняет все проверки, нужные для его открытия.
// Возвращает валидность пары для открытия.
// Последний аргумент - [...]
bool
combineAndVerify( string quoted, string base, double& fp1 )

- 是一个第三层次的函数。在这里。

open()是一个第一级函数。

pairsToOpen()是第二个函数(它被open()调用),而

combineAndVerify() - 第三次(它被pairToOpen()函数调用)。


下一级的每个函数的代码都比上一级的代码更靠左缩进。这使我们更容易看到整个程序的结构。

这条规则也有例外(有的函数被更高结构层次的两个函数调用),但这并不常见。这通常表明代码不是最优的,因为同一个动作是在程序的几个不同部分执行的。
然而,有一些通用函数,可以从任何地方调用。这些是输出功能,我把它们放在一个特殊的类别中。

3.全局变量:最好少一点,但在这里也最好不要过度。你可以把所有这些变量塞进正式的函数参数中,但这样一来,它们的调用就会写得太麻烦,而且意义模糊不清。

4.将计算的动作和它们的输出(在文件、屏幕或短信中)分开。我单独设计了所有的输出函数,然后将这些函数的调用粘贴到调用函数的主体中。

这条规则除了提高代码的清晰度和明确性外,还有另一个副作用:如果你这样做,你可以非常容易地切断代码中的所有输出,大大减少代码执行时间:输出往往是程序中最慢的动作。

5.变量名称:嗯,这很清楚。每个人都有自己的风格,但还是希望能以这样的方式来制作,以方便解释变量的含义。

我想这就够了,可以开始了。如果你愿意,你可以添加更多。
 
Mathemat >> :
我想这就够了,可以开始了。如果你愿意,你可以添加其他东西。

问题是这样的。什么是建立一个方案的最合理的方式?

1.描述一下在START函数中可以做的一切?

2) 或者我可以将所有的动作描述为用户定义的函数,然后根据需要从START函数中调用它们?

//---------------------------------------------

例如,同一个拖网。

 

第二种情况更好。这就是我所写的内容。交易函数 也应写成独立的函数。

 

对我来说,这也是差不多的。

除了。

1.函数中的行数。

2.功能的数量。

我优先考虑的是计算速度。因此,函数越少,调用的次数越少,程序运行就越快。

如果我可以摆脱一个功能,我就会使用它。

我只有一次没有做到这一点。元引号对嵌套块的数量进行了限制。

我得到了一个710行的界面渲染功能。它有51个参数。其中有21个阵列。因此,这就是Metacquotes所设法达到的目的......:-)))

一般来说,我认为只有在从程序的不同部分调用该函数时才有必要,而且不是很频繁。为了速度,我宁愿在每个区块中重复代码。

 
Zhunko >> :

其结果是一个710行的界面绘制功能。它有51个参数。其中有21个阵列。

哇。但我已经指出,输出功能代表了一个例外。至于执行速度,我认为调用一个函数而不是直接编写没有函数的正确块的代价并不大--尤其是在循环中调用函数的情况下。Rosh在某处展示了直接代码和函数调用代码之间的区别。

 
Zhunko писал(а)>>

我优先考虑的是计算速度。因此,函数越少,调用的次数越少,程序运行就越快。

如果有机会摆脱一个功能,我就利用它。

我同意。如果一个函数被调用的次数少于三次,你最好把它插入正文中。我知道它的第一手资料。我经常要编辑别人的程序。如果你有的只是功能,你必须打开两个或三个窗口,以确保你不会混淆什么时候发生。

 
Mathemat >> :

哇。但我已经指出,输出功能代表了一个例外。至于执行速度,我认为调用一个函数而不是直接编写没有函数的所需块的成本并不高--特别是如果函数是在一个循环中调用的。在某处,Rosh展示了直接代码和带有函数调用的代码之间的区别。

阿列克谢,你可能是对的,我最近没有检查,但是......!

在那些日子里,MT4的内存管理器对Metakvot肯定有一些问题。因此,在删除所有用于计算索引的函数后,我对结果感到非常惊讶!...计算速度提高5倍,内存消耗减少3倍!!!!

 
Zhunko писал(а)>>

阿列克谢,你可能是对的,最近没有检查,但是!...!

在那些日子里,MT4的内存管理器对Metakvot肯定有一些问题。因此,在删除所有用于计算指数的函数后,我对结果感到非常惊讶!...计算速度提高5倍,内存消耗减少3倍!!!!

所有在函数中声明的数组都是静态的。这意味着这些数组只被创建一次(在函数的第一次调用过程中),而且它们被存储在内存中。 因此,我试图让数组成为全局性的。这并不是好事。

 
关于函数的大小。我试图使功能适合在一个屏幕上。这样,你就可以看到整个事情。
 

是的,瓦迪姆,影响是存在的。我决定去看看。以下是结果。

1.简单的求和周期(5亿次迭代)。


int start()
{
double sum = 0;
double d;
int st = GetTickCount();
for( int i = 0; i < 500000000; i ++ )
{
add( sum );


// sum += 3.14159265;

}
int timeTotal = GetTickCount() - st;
Print( "Time = " + timeTotal );
return(0);
}
//+------------------------------------------------------------------+


double add( double sum )
{
return( sum + 3.14159265 );
}//+------------------------------------------------------------------+


计算时间(秒):4.42--不调用 add(),调用 后为36.7。


2.一个具有更复杂计算的循环(同样是5亿次迭代)。


int start()
{
double sum = 0;
double d;
int st = GetTickCount();
for( int i = 0; i < 500000000; i ++ )
{
add( i, sum, d );


// d = MathTan( i ) + MathLog( i );
// sum += MathSin( 3.14159265 );
}
int timeTotal = GetTickCount() - st;
Print( "Time = " + timeTotal );
return(0);
}//+------------------------------------------------------------------+


double add( int i, double sum, double& d )
{
d = MathTan( i ) + MathLog( i );
return( sum + MathSin( 3.14159265 ) );
}//+------------------------------------------------------------------+


计算时间(秒):100.6,不加(),142.1,加()。


这里有注释的块,在循环中直接计算,我们把它做成一个函数进行比较。正如我们所看到的,在任何情况下都有区别,但它是非常不同的。

结论是什么?如果我们把非常简单的东西组成一个函数,那么函数调用成本就会发挥重要作用,甚至非常重要。换句话说,它可能比函数体中的计算成本要高得多。如果计算比较复杂,那么该功能的存在和不存在之间的差异就会大大减少。

因此,最好只将具有或多或少严重计算的块设计成函数。我将在编码时尽量考虑到这一点。但无论如何,只有当循环有大量的迭代时,时间差异才是显著的:这里的函数调用成本约为10^(-7)秒。

原因: