1. 程式人生 > >優達學城《無人駕駛入門》學習筆記——把python翻譯成C++

優達學城《無人駕駛入門》學習筆記——把python翻譯成C++


如果你學習的第一門語言是python,可能和我當初一樣,並沒有什麼特別的感受。但是,後面又學習了C++的話就會發現,python使用起來太方便了!C++的各種條條框框太多了!

作為動態語言,python不需要預先設定資料的型別,非常靈活,而代價就是執行速度慢。與之相反,C++是靜態語言,需要宣告變數、函式或者類的型別,然後才能定義。因為C++的變數、函式和類等需要宣告和定義兩個步驟,為了使程式碼看上去更有條理,可以把它們分成兩個部分(.cpp檔案)。另外,C++可能需要匯入一些庫,這些語句可以存放在標頭檔案中(.h檔案)。

至於其他函式和語句,python和C++很接近,但又略有不同。比較好的學習方法是相互翻譯。比如把一段python程式碼翻譯成C++程式碼。

本文分成3個部分,第一部分是把一段python程式碼翻譯成C++,並且把C++程式碼分別存放在3個檔案中,目的是比較兩種語言在建立變數、函式方面的異同;第二部分則是把一個python類翻譯成C++,目的是比較兩種語言在建立類方面的異同;第三部分會推薦一些C++的學習資料,供你學習參考。

1 把python程式碼翻譯成C++,並且儲存在3個檔案中

先來看一段python程式碼。這段程式碼表達的意思是,一輛無人車沿著一維道路行走,道路分成5個格子,格子塗成綠色或者紅色。無人車在某個格子上停下,檢測格子的顏色。無人車移動的距離用move表示。無人車對格子顏色的檢測,以及移動的距離都存在誤差。正確識別顏色的概率是pHit,錯誤識別的概率是pMiss;移動正確的距離的概率是pExact,多移動一格的概率是pOvershoot,少移動一格的概率是pUndershoot.

程式碼中有sense和move兩個函式,sense用來識別無人車當前所處格子的顏色,並且計算位於每個格子的概率;move用來計算無人車移動後,位於每個格子的概率。

# 無人車停在每個格子裡面的初始概率
p = [0.2, 0.2, 0.2, 0.2, 0.2]
# 建立一維道路
world = ['green', 'red', 'red', 'green', 'green']
# 無人車測量到的顏色
measurements = ['red', 'green']
# 無人車移動的距離
motions = [1,1]
# 正確識別顏色的概率
pHit = 0.6
# 錯誤識別顏色的概率
pMiss = 0.2
# 移動正確距離的概率 pExact = 0.8 # 多移動一格的概率 pOvershoot = 0.1 # 少移動一格的概率 pUndershoot = 0.1 # 識別當前格子的顏色,計算無人車位於每個格子的概率 def sense(p, Z): q=[] for i in range(len(p)): hit = (Z == world[i]) q.append(p[i] * (hit * pHit + (1-hit) * pMiss)) # 歸一化 s = sum(q) for i in range(len(q)): q[i] = q[i] / s return q # 移動後無人車位於每個格子的概率 def move(p, U): q = [] for i in range(len(p)): s = pExact * p[(i-U) % len(p)] s = s + pOvershoot * p[(i-U-1) % len(p)] s = s + pUndershoot * p[(i-U+1) % len(p)] q.append(s) return q # 無人車識別、移動兩次後,位於每個格子的概率 for k in range(len(measurements)): p = sense(p, measurements[k]) p = move(p, motions[k]) print(p) [0.21157894736842103, 0.1515789473684211, 0.08105263157894739, 0.16842105263157897, 0.3873684210526316]

把程式碼翻譯成C++.

注意:程式碼分成了4個部分,依次是:

  1. 匯入庫、使用名稱空間;
  2. 宣告、定義變數,宣告函式;
  3. main函式;
  4. 定義函式。
// 匯入庫
#include <iostream>
#include <vector>
#include <string>
// 名稱空間
using namespace std;

/* 宣告、定義變數。
 * 注意,不論是變數、函式還是類,都需要標明資料型別。
 * 小數預設是double,如果宣告、初始化float,
 * 需要在小數後面加上“f”
 */
float pHit = 0.6f;
float pMiss = 0.2f;
float pExact = 0.8f;
float pOvershoot = 0.1f;
float pUndershoot = 0.1f;
// 宣告、定義向量
vector<float> p(5, 0.2f);
vector<string> world = { "green", "red", "red", "green", "green" };
vector<string> measurements = { "red", "green" };
vector<int> motions = { 1, 1 };
// 宣告函式
vector<float> sense(vector<float> p, string Z);
vector<float> move(vector<float> p, int U);

// main函式,實現無人車識別、移動,並且計算概率
int main() 
{
    /* 注意C++的for語句和python的用法不同,
     * C++的for其實更類似於python的while
     */
	for (unsigned int k = 0; k < measurements.size(); k++) 
    {
		p = sense(p, measurements[k]);
		p = move(p, motions[k]);
	}
	for (unsigned int i = 0; i < p.size(); i++) 
    {
        // C++可以通過cout輸出數字、字元等
		cout << p[i] << endl;
	}
    // 換行。和python不同,C++不能自動換行
	cout << endl;
	return 0;
}

// 定義sense函式
vector <float> sense(vector <float> p, string Z) 
{
	vector <float> q;
	for (unsigned int i = 0; i < p.size(); i++) 
    {
		int hit = (Z == world[i]);
        // C++向量的push_back方法,類似python列表的append方法
		q.push_back(p[i] * (hit * pHit + (1 - hit)*pMiss));
	}
	float s = 0;
    // 遍歷向量的每一個元素,這種用法和python的for類似
	for (float i : q) 
    {
		s = s + i;
	}
    // C++向量的size方法,類似python的len函式
	for (unsigned int i = 0; i < q.size(); i++) 
    {
		q[i] = q[i] / s;
	}
	return q;
}

// 定義move函式
vector <float> move(vector <float> p, int U) 
{
	vector <float> q;
	for (unsigned int i = 0; i < p.size(); i++) 
    {
		float s;
	    /* 注意取餘%的用法,C++和python是不同,
         * python的取餘總是大於等於0,
         * 而C++可以是負數。如果希望結果大於等於0,可以加上一個週期的長度
         */
		s = pExact * p[(i - U + p.size()) % p.size()];
		s = s + pOvershoot * p[(i - U - 1 + p.size()) % p.size()];
		s = s + pUndershoot * p[(i - U + 1 + p.size()) % p.size()];
		q.push_back(s);
	}
	return q;
}
    
0.211579
0.151579
0.0810526
0.168421
0.387368

如果把程式碼放在3個檔案中,程式碼如下:

function.h檔案,又叫做標頭檔案,包含匯入庫、使用名稱空間、宣告函式。

// ifndef、define和endif的作用是避免標頭檔案被重複匯入
#ifndef FUNCTION_H
#define FUNCTION_H

#include <iostream>
#include <vector>
#include <string>

// 宣告函式時用到了vector,需要宣告名稱空間
using namespace std;

// 宣告函式
// 注意:一般不在標頭檔案中宣告、定義變數,會報錯
vector <float> sense(vector <float> p, string Z);
vector <float> move(vector <float> p, int U);
// 避免標頭檔案被重複匯入
#endif /*FUNCTION_H*/

function.cpp檔案,包含:

  1. 匯入標頭檔案;
  2. 宣告、定義變數;
  3. 定義函式。

注意:function.cpp檔案和main.cpp檔案都存在變數的宣告、定義。一個變數到底在哪個檔案裝宣告和定義,關鍵是看哪個檔案呼叫了它。比如,變數pHit,pMiss,pExact,pOvershoot,pUndershoot在函式sense和move中被呼叫了(不考慮函式宣告的呼叫),所以它們被放在定義了函式sense和move的檔案中;而main函式呼叫了變數p,my_vector, measurements ,motions,所以它們被放在main.cpp檔案中。

// 匯入標頭檔案
#include "function.h"

// 宣告、定義變數
vector<string> world = { "green", "red", "red", "green", "green" };
float pHit = 0.6f;
float pMiss = 0.2f;
float pExact = 0.8f;
float pOvershoot = 0.1f;
float pUndershoot = 0.1f;

// 定義函式
vector <float> sense(vector <float> p, string Z) 
{
	vector <float> q;
	for (int i = 0; i < p.size(); i++) 
	{
		int hit = (Z == world[i]);
		q.push_back(p[i] * (hit * pHit + (1 - hit)*pMiss));
	}
	float s = 0;
	for (float i : q) 
	{
		s = s + i;
	}
	for (int i = 0; i < q.size(); i++) 
	{
		q[i] = q[i] / s;
	}
	return q;
}

vector <float> move(vector <float> p, int U) 
{
	vector <float> q;
	for (unsigned int i = 0; i < p.size(); i++) 
	{
		float s;
		/* C++的取餘和python的不同,python的取餘總是大於等於0,
		 * 而C++還可以是負數。如果希望結果大於等於0,
		 * 要加上一個週期的長度
		*/
		s = pExact * p[(i - U + p.size()) % p.size()];
		s = s + pOvershoot * p[(i - U - 1 + p.size()) % p.size()];
		s = s + pUndershoot * p[(i - U + 1 + p.size()) % p.size()];
		q.push_back(s);
	}
	return q;
}

main.cpp,包含:

  1. 匯入標頭檔案;
  2. 宣告、定義變數;
  3. main函式。
#include "function.h"

// 宣告、定義變數
vector<float> p(5, 0.2f);
vector<float> my_vector = { 1,2,3,4 };
vector<string> measurements = { "red", "green" };
vector<int> motions = { 1, 1 };

int main() {
	for (int k = 0; k < measurements.size(); k++) 
	{
		p = sense(p, measurements[k]);
		p = move(p, motions[k]);
	}
	for (int i = 0; i < p.size(); i++) 
	{
		cout << p[i] << endl;
	}
	cout << endl;
	return 0;
}

把python類翻譯成C++

"""
兩個高斯分佈的概率密度函式相乘,計算新的概率密度函式的期望和方差
"""
from math import *
class Gaussian(object):
    def __init__(self, average, variance):
        # 屬性名稱前面加上雙下劃線"__"表示私有變數
        self.__mu = average
        self.__sigma2 = variance

    # 設定私有變數
    def setMu(self, average):
        self.__mu = average

    def setSigma2(self, variance):
        self.__sigma2 = variance

    # 訪問私有變數
    def getMu(self):
        return self.__mu

    def getSigma2(self):
        return self.__sigma2

    def evaluate(self, x):
        coefficient = 1.0 / sqrt(2.0 * pi * self.__sigma2)
        exponential = exp(-0.5 * (x-self.__mu)**2 / self.__sigma2)
        return coefficient * exponential

    def multiply(self, other):
        # 計算新函式的期望
        denominator = self.__sigma2 + other.__sigma2
        numerator = self.__mu * other.__sigma2 + other.__mu * self.__sigma2
        new_mu = numerator / denominator
        # 計算新函式的方差
        new_sigma2 = 1.0 / ((1.0 / self.__sigma2) + (1.0 / other.__sigma2))
        # 生成新的高斯分佈,返回類的例項
        return Gaussian(new_mu, new_sigma2)

    def add(self, other):
        new_mu = self.__mu + other.__mu
        new_sigma2 = self.__sigma2 + other.__sigma2
        return Gaussian(new_mu, new_sigma2)

mygaussian = Gaussian(30.0, 20.0)
othergaussian = Gaussian(10.0, 30.0)

print("average ",  mygaussian.getMu())
print("evaluation ", mygaussian.evaluate(15.0))
print("mul results sigma ", mygaussian.multiply(othergaussian).getSigma2())
print("mul results average ", mygaussian.multiply(othergaussian).getMu())
print("add results sigma ", mygaussian.add(othergaussian).getSigma2())
print("add results average ", mygaussian.add(othergaussian).getMu())

C++類

gaussian.h

#ifndef GAUSSIAN_H
#define GAUSSIAN_H

#include <iostream>
#include <math.h>

class Gaussian
{
private:
	float mu, sigma2;

public:
	Gaussian();
	Gaussian(float average, float variance);
	void setMu(float average);
	void setSigma2(float variance);
	float getMu();
	float getSigma2();
	float evaluate(float x);
	Gaussian multiply(Gaussian other);
	Gaussian add(Gaussian other);
};

# endif GAUSSIAN_H

gaussian.cpp

#include "gaussian.h"

Gaussian::Gaussian()
{
	mu = 0;
	sigma2 = 1;
}

Gaussian::Gaussian(float average, float variance)
{
	mu = average;
	sigma2 = variance;
}

void Gaussian::setMu(float average)
{
	mu = average;
}

void Gaussian::setSigma2(float variance)
{
	sigma2 = variance;
}

float Gaussian::getMu()
{
	return mu;
}

float Gaussian::getSigma2()
{
	return sigma2;
}

float Gaussian::evaluate(float x)
{
	float coefficient;
	float exponential;
	coefficient = 1.0 / sqrt(2.0 * 3.14 * sigma2);
	exponential = exp(-0.5 * pow((x - mu), 2) / sigma2);
	return coefficient * exponential;
}

Gaussian Gaussian::multiply(Gaussian other)
{
	float denominator, numerator, new_mu, new_sigma2;
	denominator = sigma2 + other.sigma2;
	numerator = mu * other.sigma2 + other.mu * sigma2;
	new_mu = numerator / denominator;
	new_sigma2 = 1.0 / ((1.0 / sigma2) + (1.0 / other.sigma2));
	return Gaussian(new_mu, new_sigma2);
}

Gaussian Gaussian::add(Gaussian other)
{
	float new_mu, new_sigma2;
	new_mu = mu + other.mu;
	new_sigma2 = sigma2 + other.sigma2;
	return Gaussian(new_mu, new_sigma2);
}

main.cpp

#include "gaussian.h"

int main()
{
	// 建立預設例項
	Gaussian fun1;

	// 給例項賦值
	Gaussian mygaussian(30.0, 20.0);
	Gaussian othergaussian(10.0, 30.0);

	std::cout << "average " << mygaussian.getMu() << std::endl;
	std::cout << "evaluation " << mygaussian.evaluate(15.0) << std::endl;

	std::cout << "mul results sigma " << mygaussian.multiply(othergaussian).getSigma2() << std::endl;
	std::cout << "mul results average " << mygaussian.multiply(othergaussian).getMu() << std::endl;

	std::cout << "add results sigma " << mygaussian.add(othergaussian).getSigma2() << std::endl;
	std::cout << "add results average " << mygaussian.add(othergaussian).getMu() << std::endl;

	return 0;
}

2 把python類翻譯成C++

明白瞭如何把python程式碼翻譯成C++,我們再來看看python類和C++類的異同。

下面是python程式碼編寫的一個類Gaussian,目的是進行兩個概率密度函式之間的計算。

# 匯入math庫
from math import *

# 定義Gaussian類
class Gaussian(object):
    # 建構函式
    def __init__(self, average, variance):
        # 屬性名稱前面加上雙下劃線"__"表示私有變數
        self.__mu = average
        self.__sigma2 = variance

    # 設定私有變數。
    # 外界無法賦值私有變數,需要通過該函式
    def setMu(self, average):
        self.__mu = average

    def setSigma2(self, variance):
        self.__sigma2 = variance

    # 訪問私有變數。
    # 外界無法訪問私有變數,需要通過該函式
    def getMu(self):
        return self.__mu

    def getSigma2(self):
        return self.__sigma2
	
    # 輸入概率密度函式的x值,返回y值
    def evaluate(self, x):
        coefficient = 1.0 / sqrt(2.0 * pi * self.__sigma2)
        exponential = exp(-0.5 * (x-self.__mu)**2 / self.__sigma2)
        return coefficient * exponential
	
    # 兩個概率密度函式相乘,
    # 返回新的概率密度函式的例項
    def multiply(self, other):
        # 計算新函式的期望
        denominator = self.__sigma2 + other.__sigma2
        numerator = self.__mu * other.__sigma2 + other.__mu * self.__sigma2
        new_mu = numerator / denominator
        # 計算新函式的方差
        new_sigma2 = 1.0 / ((1.0 / self.__sigma2) + (1.0 / other.__sigma2))
        # 生成新的高斯分佈,返回類的例項
        return Gaussian(new_mu, new_sigma2)
	
    # 兩個概率密度函式相加,返回新的概率密度函式的實體
    def add(self, other):
        new_mu = self.__mu + other.__mu
        new_sigma2 = self.__sigma2 + other.__sigma2
        return Gaussian(new_mu, new_sigma2)

# 實體化
mygaussian = Gaussian(30.0, 20.0)
othergaussian = Gaussian(10.0, 30.0)

# 通過getMu訪問私有變數
print("average ",  mygaussian.getMu())
average  30.0

print("evaluation ", mygaussian.evaluate(15.0))
evaluation  0.00032172781336966156

print("mul results sigma ", mygaussian.multiply(othergaussian).getSigma2())
mul results sigma  11.999999999999998

print("mul results average ", mygaussian.multiply(othergaussian).getMu())
mul results average  22.0

print("add results sigma ", mygaussian.add(othergaussian).getSigma2())
add results sigma  50.0

print("add results average ", mygaussian.add(othergaussian).getMu())
add results average  40.0

把上面的程式碼翻譯成C++類

gaussian.h檔案:宣告類

#ifndef GAUSSIAN_H
#define GAUSSIAN_H

#include <iostream>
#include <math.h>

class Gaussian
{
	// 宣告私有成員
    private:
        float mu, sigma2;

    // 宣告公開成員
    public:
    	// 宣告預設建構函式
        Gaussian();
    	// 宣告建構函式
        Gaussian(float average, float variance);
    	// 宣告函式
        void setMu(float average);
        void setSigma2(float variance);
        float getMu();
        float getSigma2();
        float evaluate(float x);
        Gaussian multiply(Gaussian other);
        Gaussian add(Gaussian other);
};

# endif GAUSSIAN_H

gaussian.cpp檔案:定義類

#include "gaussian.h"

// 定義預設建構函式,變數mu和sigma賦予了預設值
Gaussian::Gaussian()
{
	mu = 0;
	sigma2 = 1;
}

// 定義建構函式,變數mu和sigma沒有賦值
Gaussian::Gaussian(float average, float variance)
{
	mu = average;
	sigma2 = variance;
}

// 定義函式
void Gaussian::setMu(float average)
{
	mu = average;
}

void Gaussian::setSigma2(float variance)
{
	sigma2 = variance;
}

float Gaussian::getMu()
{
	return mu;
}

float Gaussian::getSigma2()
{
	return sigma2;
}

float Gaussian::evaluate(float x)
{
	float coefficient;
	float exponential;
	coefficient = 1.0 / sqrt(2.0 * 3.14 * sigma2);
	exponential = exp(-0.5 * pow((x - mu), 2) / sigma2);
	return coefficient * exponential;
}

Gaussian Gaussian::multiply(Gaussian other)
{
	float denominator, numerator, new_mu, new_sigma2;
	denominator = sigma2 + other.sigma2;
	numerator = mu * other.sigma2 + other.mu * sigma2;
	new_mu = numerator / denominator;
	new_sigma2 = 1.0 / ((1.0 / sigma2) + (1.0 / other.sigma2));
	return Gaussian(new_mu, new_sigma2);
}

Gaussian Gaussian::add(Gaussian other)
{
	float new_mu, new_sigma2;
	new_mu = mu + other.mu;
	new_sigma2 = sigma2 + other.sigma2;
	return Gaussian(new_mu, new_sigma2);
}

main.cpp

#include "gaussian.h"

int main()
{
	// 建立預設例項。這裡呼叫的是預設建構函式
	Gaussian fun1;

	// 建立例項,並且給例項賦值。這裡呼叫的是建構函式
	Gaussian mygaussian(30.0, 20.0);
	Gaussian othergaussian(10.0, 30.0);

	std::cout << "average " << mygaussian.getMu() << std::endl;
	std::cout << "evaluation " << mygaussian.evaluate(15.0) << std::endl;

	std::cout << "mul results sigma " << mygaussian.multiply(othergaussian).getSigma2() << std::endl;
	std::cout << "mul results average " << mygaussian.multiply(othergaussian).getMu() << std::endl;

	std::cout << "add results sigma " << mygaussian.add(othergaussian).getSigma2() << std::endl;
	std::cout << "add results average " << mygaussian.add(othergaussian).getMu() << std::endl;

	return 0;
}

3 補充學習資料

python類是一個非常重要的概率,在《無人駕駛入門》以及無人駕駛領域,都有著非常重要的應用。而課程對C++的要求卻並不高。這是因為在無人駕駛領域,編寫程式碼使用的主要還是python,但是因為C++執行速度快,所以最終需要把python程式碼翻譯成C++.下面推薦一些python類和C++的學習資料。

我是《無人駕駛入門》納米學位的學長,希望這些經驗對你有幫助。如果你對udacity的這門課程也感興趣,可以使用我的優惠碼:839662C0,付款時在優惠碼框輸入,可以抵扣300元學費(限第一次購買udacity課程的學弟學妹用哈)。