svm.cpp淺談
svm.cpp總共有3159行代碼,實現了svm算法的核心功能,裏面總共有Cache、Kernel、ONE_CLASS_Q、QMatrix、Solver、Solver_NU、SVC_Q、SVR_Q 8個類(以下圖1所示),而它們之間的繼承和組合關係如圖二、圖3所示。在這些類中Cache、Kernel、Solver是核心類,對整個算法起支撐做用。在之後的博文中咱們將對這3個核心類作重點註解分析,另外還將對svm.cpp中的svm_train函數作一個註解分析。web
圖1算法
圖2app
圖3函數
下面先看看svm.cpp的前幾行代碼,主要是幾個inline函數,inline函數解決頻繁調用小函數大量消耗棧空間的問題。
- #include "svm.h"
- int libsvm_version = LIBSVM_VERSION;
-
- typedef float Qfloat;
- typedef signed char schar;
-
- //inline函數解決頻繁調用小函數大量消耗棧空間;static函數爲靜態函數/或內部函數,指函數的調用範圍只侷限於本文件
- #ifndef min
- template <class T> static inline T min(T x,T y) { return (x<y)?x:y; }
- #endif
- #ifndef max
- template <class T> static inline T max(T x,T y) { return (x>y)?x:y; }
- #endif
- template <class T> static inline void swap(T& x, T& y) { T t=x; x=y; y=t; }
下面這個是clone函數(
一個徹底克隆函數,操做結束後,內部的全部數據和指針徹底同樣),剛開始看的時候對clone的形參T*& dst不太瞭解,後來才知道這是指針的引用,就是說在函數裏面指針的改變等同於形參的改變。關於
memcpy和*&的具體解釋我參看個人博文
常見的C++知識 。
- template <class S, class T> static inline void clone(T*& dst, S* src, int n)
- {
- dst = new T[n];
- memcpy((void *)dst,(void *)src,sizeof(T)*n);
- }
下面這個是冪次函數,寫得挺好的,
對冪次爲偶數有優化。當爲powi(3,5)時,是這樣的3*(81),即把3^5=3*(3^4)
- static inline double powi(double base, int times)
- {
- double tmp = base, ret = 1.0;
-
- for(int t=times; t>0; t/=2)
- {
- if(t%2==1) ret*=tmp;
- tmp = tmp * tmp;
- }
- return ret;
- }
如下幾個是調試信息輸出
- static void print_string_stdout(const char *s)
- {
- fputs(s,stdout);
- fflush(stdout);
- }
- static void (*svm_print_string) (const char *) = &print_string_stdout;
-
- #if 1
- static void info(const char *fmt,...)
- {
- char buf[BUFSIZ];
- va_list ap;
- va_start(ap,fmt);
- vsprintf(buf,fmt,ap);
- va_end(ap);
- (*svm_print_string)(buf);
- }
- #else
- static void info(const char *fmt,...) {}
- #endif
函數指針專題
在libsvm中有以下代碼:oop
- static void print_string_stdout(const char *s)
- {
- fputs(s,stdout);
- fflush(stdout);
- }
- static void (*svm_print_string) (const char *) = &print_string_stdout;
我在論壇中曾經發帖:學習
看了Pump每天學習同窗的回覆,我纔回想起來:第六行那原來是個函數指針,平時沒用到,就給忘了,只能拿出教材從新來學習一遍:優化
函數指針是指向函數而非指向對象的指針。像其餘指針同樣,函數指針也指向某個特定的類型。函數類型由其返回類型以及形參表肯定,而與函數名無關:ui
- // pf points to function returning bool thar takes two const string references
- bool (*pf) (const string &, const string &);
這個語句將pf聲明爲指向函數的指針,它所指向的函數帶有兩個const string &類型的形參和bool類型的返回值。spa
注意:*pf兩側的圓括號是必需的,若沒有,則就不是函數指針了。.net
- // declares a function named pf that returns a bool*
- bool *pf (const string &, const string &)
typedef簡化函數指針的定義
函數指針類型至關冗長。使用typedef爲指針類型定義同義詞,可將函數指針的使用大大簡化
- typedef bool (*cmpFn)(const string &, const string &)
該定義表示cmpFn是一種指向函數的指針類型的名字。該指針類型爲:「指向返回bool類型並帶有兩個const string 引用形參的函數的指針」。在要使用這種函數指針類型時,只需直接使用cmpFn便可,沒必要每次都把整個函數類型聲明所有寫出來。
經過指針調用函數
指向函數的指針可用於調用它所指向的函數,能夠不須要使用解引用操做符,直接經過指針調用函數:
- bool lengthCmp(const string&, const string &);
- cmpFn pf = lengthCmp;
- lengthCmp=("hi","bye");//direct call
- pf("hi","bye");//equivalent call: pf1 implicitly dereferenced
- (*pf)("hi","bye");//equivalent call: pf1 explicitly dereferenced