kodlama stili hakkında

 

MQL4'te "sıfırdan" yazılmış bir şeyi kodlama ve kod dönüştürme konusunda uzun süredir iyi bir deneyimim olduğu için konuyu gündeme getiriyorum ve deneyimlerimi paylaşmak istiyorum.

Meslektaşım, belirli bir algoritmayı uygulayan bir programı hızlı bir şekilde yazma yeteneğinizden şüphe duymuyorum. Ancak, bir nedenden dolayı projeyi terk ettiyseniz, bir ay sonra ona geri döndüyseniz ve hemen anlayamadıysanız, o zaman kötü yazdığınızdan emin oldum. Ardından, kendi geliştirdiğim kod tasarımı stili için gereksinimleri belirleyeceğim. Bu gereksinimlere uygunluk, bence, daha fazla modifikasyonunu büyük ölçüde basitleştiriyor.

0. Hemen savaşa girmek için acele etmeyin ve bir program yazın. Klasiklerin önerdiği gibi yapın: Programın yapısını düşünerek birkaç saat geçirin. Sonra oturup hızlı ve net bir program yazabilirsiniz. Bu birkaç saat, yazma hızında ve birçok kez daha fazla hata ayıklamada karşılığını verecektir.

1. Fonksiyonların uzunluğu 20 satırı önemli ölçüde aşmamalıdır. Bunu uygulayamazsanız, bir yerlerde kodun mantığını ve yapısını yeterince iyi düşünmediğiniz yerler vardır. Ayrıca, kod hatalarını ayıklarken genellikle en fazla zaman alan, en uzun işlevler ve onların çağırdıkları işlevlerle olan ilişkileridir.

Örneğin, şu anda kodum 629 satır uzunluğunda ve 27 işlevi var. Bu, işlev çağrısının yapısının açıklaması (2-6 satır) ve her işlevden önce kısa bir açıklamanın yanı sıra işlevler arasında 4-5 satırlık boş ayırıcılarla birlikte. Ek olarak, blok parantezleri (kıvırcık) stint olmadan koydum, yani. iki parantezin her biri için bir çizgi alıyorum. Fonksiyonlardan önceki tüm yorumları kaldırır ve fonksiyonlar arasındaki ayırıcı sayısını azaltırsak, 27 fonksiyon için yaklaşık 400 satır kalır, yani. ortalama fonksiyon uzunluğum yaklaşık 15 satırdır.

Elbette bu kuralın istisnaları vardır - ancak bu, en basit işlevler veya çıktı işlevleri için geçerlidir. Genel olarak konuşursak, bir işlev, işlevsel olarak farklı 3-5 eylemden fazlasını gerçekleştirmemelidir. Aksi takdirde kafa karıştırıcı olacaktır. Bir işlev içindeki işlevsel olarak farklı eylemler arasına genellikle boş bir satır koyarım.

Yalnızca 4'ten (bu, işlev gövdesinde bir satır, bildirim satırı başına bir ve kaşlı ayraç başına iki olmak üzere minimum değerdir) 10 satıra kadar değişen birkaç işlevim var. Bundan dolayı kod hızının düşürülmesi konusunda endişelenmiyorum, çünkü kod aslında bu nedenle değil, çarpık eller nedeniyle yavaşlar.

2. Eylemlerin, değişkenlerin ve işlevlerin anlamlarını açıklayan yorumları eksik etmeyin. Fonksiyonun uzunluğundaki 20 satırlık sınır sarsılmaz kalır. Hala bozulursa, işlevi yeniden yazın. Yorumlar tek satırlı veya çok satırlı olabilir.

3. İşlevlerim yapılandırılmıştır. En yüksek (sıfır) çağrı seviyesi, 1., 2. vb. fonksiyonlar vardır. Bir sonraki çağrı düzeyinin her işlevi, doğrudan bir önceki düzeyin işlevi tarafından çağrılır. Örneğin, işte bir işlev:

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

- Bu, üçüncü seviyenin bir işlevidir. Burada:

open() - birinci seviye fonksiyon,

pairsToOpen() - ikincisi (open() işlevi tarafından çağrılır) ve

CombineAndVerify() üçüncüdür (pairsToOpen() işlevi tarafından çağrılır).


Sahip olduğum bir sonraki seviyenin her bir fonksiyonunun kodu, bir öncekinin fonksiyonunun kodundan daha sol kenardan girintilidir. Bu şekilde tüm programın yapısını daha iyi görebilirsiniz.

Bu kuralın istisnaları da vardır (daha yüksek bir yapısal seviyedeki iki fonksiyon tarafından çağrılan fonksiyonlar vardır), ancak bu nadirdir. Bu genellikle kodun optimal olmadığını gösterir, çünkü aynı eylem, programın birkaç farklı bölümünde gerçekleştirilir.
Ancak, herhangi bir yerden çağrılabilen evrensel işlevler vardır. Bunlar çıkarım fonksiyonları, onları özel bir kategoriye koydum.

3. Global değişkenler : Daha azına sahip olmak daha iyidir, ancak burada da aşırıya kaçmamak daha iyidir. Bu tür tüm değişkenleri fonksiyonların biçimsel parametrelerine sokmak mümkündür, ancak o zaman çağrıları yazmak için çok hantal ve anlam açısından belirsiz olacaktır.

4. Ayrı hesaplama ve çıktı işlemleri (dosyaya, ekrana veya SMS'e). Tüm çıktı işlevlerini ayrı ayrı yazarım ve sonra bu tür işlevlere çağrıları çağıran işlevin gövdesine eklerim.

Bu kuralın, kodun netliğini ve okunabilirliğini iyileştirmeye ek olarak, başka bir yan etkisi daha vardır: Bunu yaparsanız, koddan tüm çıktıları çok kolay bir şekilde kesebilir ve kod yürütme süresini önemli ölçüde azaltabilirsiniz: çıktı genellikle programdaki en yavaş eylem.

5. Değişkenlerin isimleri: peki, burada her şey açık. Herkesin kendi stili vardır, ancak yine de değişkenlerin anlamını kolayca açıklayabilmeleri için bunları yapmak istenir.

Başlamak için bu kadarının yeterli olduğunu düşünüyorum. Dileyen başka bir şey ekleyebilir.
 
 
Mathemat >> :
Başlamak için bu kadarının yeterli olduğunu düşünüyorum. Dileyen başka bir şey ekleyebilir.

Soru. Bir program oluşturmanın en iyi yolu nedir?

1. BAŞLAT işlevinde mümkün olan her şeyi açıklayın?

2. Veya tüm eylemleri kullanıcı tanımlı işlevler olarak yazın ve ardından gerektiğinde bunları BAŞLAT işlevinden mi çağırın?

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

Örneğin, aynı trol.

 

İkincisi daha iyi. Bu konuda yazıyorum. Ticaret fonksiyonlarının ayrı fonksiyonlar olarak yazılması da arzu edilir.

 

Neredeyse her şeyim aynı.

Nın istisnası ile:

1. Fonksiyondaki satır sayısı.

2. Fonksiyonların sayısı.

Önceliğim hız. Bu nedenle, ne kadar az işlev ve onları ne kadar az çağırırsanız, program o kadar hızlı çalışır.

Fonksiyondan kurtulmak için bir fırsat varsa, bu fırsatı kullanırım.

Sadece bir kez başarısız oldu. Metakotalar, iç içe blokların sayısına bir sınır getirmiştir.

Sonuç, 710 satırlık arayüz çizmek için bir fonksiyondu. 51 parametresi vardır. Bunlardan 21 dizi. Metaquotes'ın getirdiği şey bu ... :-)))

Genel olarak, bir fonksiyonun sadece programın farklı bölümlerinden çağrılırsa ve çok sık olmasa da gerekli olduğunu düşünüyorum. Hız için her blokta kodu tekrarlamayı tercih ederim.

 
Zhunko >> :

Sonuç, 710 satırlık arayüz çizmek için bir fonksiyondu. 51 parametresi vardır. Bunlardan 21 dizi.

Vay. Ama çıkarım işlevleri, daha önce de belirtmiştim, bir istisnadır. Yürütme hızı açısından, bana öyle geliyor ki, istenen bloğu bir işlev olmadan doğrudan yazmak yerine bir işlevi çağırmanın ek yükü o kadar büyük değil - özellikle işlev bir döngü içinde çağrılıyorsa. Bir yerde Rosh, doğrudan kod ile kod arasındaki farkı bir işlev çağrısıyla gösterdi.

 
Zhunko писал(а) >>

Önceliğim hız. Bu nedenle, ne kadar az fonksiyon ve ne kadar az çağırırsanız, program o kadar hızlı çalışır.

Fonksiyondan kurtulmak için bir fırsat varsa, bu fırsatı kullanırım.

Kabul ediyorum. İşlev üç defadan daha az çağrılırsa, onu gövdeye yerleştirmek daha iyidir. İlk elden tanıdık. Genellikle başkalarının programlarını düzenlemeniz gerekir. Tüm işlevler varsa, ne zaman ne olacağı konusunda kafanızı karıştırmamak için iki hatta üç pencere açmanız gerekir.

 
Mathemat >> :

Vay. Ama çıkarım işlevleri, daha önce de belirtmiştim, bir istisnadır. Yürütme hızı açısından, bana öyle geliyor ki, istenen bloğu bir işlev olmadan doğrudan yazmak yerine bir işlevi çağırmanın ek yükü o kadar büyük değil - özellikle işlev bir döngü içinde çağrılıyorsa. Bir yerde Rosh, doğrudan kod ile kod arasındaki farkı bir işlev çağrısıyla gösterdi.

Alexei, belki haklısın, son zamanlarda inanmadım, AMA! ...

Muhtemelen, o günlerde Metaquotes'un MT4'teki bellek yöneticisiyle ilgili bazı sorunları vardı. Böylece, indeksleri hesaplamak için kullanılan tüm fonksiyonları kaldırdıktan sonra sonuca çok şaşırdım!... Hesaplama hızı 5 kat arttı ve bellek tüketimi 3 kat azaldı!!!

 
Zhunko писал(а) >>

Alexei, belki haklısın, son zamanlarda inanmadım, AMA! ...

Muhtemelen, o günlerde Metaquotes'un MT4'teki bellek yöneticisiyle ilgili bazı sorunları vardı. Böylece, indeksleri hesaplamak için kullanılan tüm fonksiyonları kaldırdıktan sonra sonuca çok şaşırdım!... Hesaplama hızı 5 kat arttı ve bellek tüketimi 3 kat azaldı!!!

İşlevlerde bildirilen tüm diziler statiktir. Bu, bu dizilerin yalnızca bir kez (ilk işlev çağrısında) oluşturulduğu ve bellekte depolandığı anlamına gelir. Bu yüzden dizileri global hale getirmeye çalışıyorum. İyi olmayan ne.

 
İşlev boyutu. Fonksiyonu bir ekrana sığdırmaya çalışıyorum. Hepsini görebilmek için.
 

Evet Vadim , etki var. Kontrol etmeye karar verdi. Sonuçlar burada:

1. Basit toplama döngüsü (500 milyon yineleme):


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 );
}//+------------------------------------------------------------------+


Saniye cinsinden hesaplama süreleri: 4,42 - add() işlevini çağırmadan , 36.7'yi çağırarak.


2. Daha karmaşık hesaplamalarla döngü (aynı 500 milyon yineleme):


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 ) );
}//+------------------------------------------------------------------+


Saniye cinsinden hesaplama süresi: 100,6 - add() işlevini çağırmadan, çağrısıyla 142.1.


Burada, karşılaştırma için bir fonksiyona biçimlendirdiğimiz, döngüde doğrudan hesaplamaları olan bloklar yorumlanır. Gördüğünüz gibi, her durumda bir fark var, ama çok farklı.

Sonuçlar nelerdir? Çok basit bir şeyi bir işleve sarıyorsak, işlevi çağırmanın maliyeti önemli bir rol oynar, hatta çok fazla. Onlar. fonksiyon gövdesindeki hesaplamaların maliyetinden çok daha fazla olabilirler. Hesaplamalar daha karmaşıksa, bir fonksiyonun varlığı ile yokluğu arasındaki fark keskin bir şekilde azalır.

Bu nedenle, bir fonksiyonda sadece az ya da çok ciddi hesaplamaları olan blokları düzenlemek daha iyidir. Kod yazarken bunu dikkate almaya çalışacağım. Ancak her durumda, zamandaki fark yalnızca döngüde çok fazla yineleme olduğunda önemlidir: burada işlevi çağırmanın maliyeti yaklaşık 10 ^ (-7) saniye oldu.