熱門文章

2023年10月6日 星期五

C++ 呼叫自訂函式修改二維 array 及 vector

作者:王一哲
日期:2023年10月6日



前言


由於 Python 的自訂函式可以回傳 list,如果要呼叫自訂函式修改 list 的內容相當簡單,只要將 list 的內容重設為回傳值即可,例如以下的程式碼:
# 修改1維 list
def myfunc(a):
    a[2] = -1
    return a

def myprint(a):
    for i in range(len(a)):
        print(a[i], end=" " if i < len(a)-1 else "\n")

data = [1]*5  # 內容為 [1, 1, 1, 1, 1]
print("1D List")
myprint(data)
print("Modify 1D List")
data = myfunc(data)  # 內容變為 [1, 1, -1, 1, 1]
myprint(data)

# 修改2維 list
def myfunc(a):
    a[2][2] = -1
    return a

def myprint(a):
    for i in range(len(a)):
        for j in range(len(a[i])):
            print(a[i][j], end=" " if j < len(a[i])-1 else "\n")

data = [[1]*5 for _ in range(3)]  # 內容為 [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]
print("2D List")
myprint(data)
print("Modify 2D List")
data = myfunc(data)  # 內容變為 [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, -1, 1, 1]]
myprint(data)


但是 C++ 呼叫自訂函式時,無法回傳 array 或 vector,如果要使用自訂函式修改 array 或 vector 的內容,需要使用傳指標呼叫 (*) 或是 傳參考呼叫 (&),以下是簡單的例子。


修改 array


1維 array


建立 array 時需要使用指標,再用 new 分配記憶體給 array,最後用 for 迴圈設定 array 的內容。呼叫自訂函式修改 array 內容時,要使用傳指標呼叫,不需要回傳值。
#include <iostream>
using namespace std;

void myfunc(int*);
void myprint(int*, int);

int main() {
    int n = 5;
    int *data;  // 不能直接使用 int data[n]
    data = new int [n];
    for(int i=0; i<n; i++) data[i] = 1;  // 內容為 {1, 1, 1, 1, 1}
    cout << "1D Array" << endl;
    myprint(data, n);
    cout << "Modify 1D Array" << endl;
    myfunc(data);  // 內容變為 {1, 1, -1, 1, 1}
    myprint(data, n);
    return 0;
}

/* 自訂函式,呼叫後將索引值為2的元素改為-1 */
void myfunc(int *a) {  
    a[2] = -1;
}

/* 自訂函式,印出 array 內容,用空格分隔,印出最後一個元素時換行 */
void myprint(int *a, int n) {
    for(int i=0; i<n; i++) {
        cout << a[i] << " \n"[i == n-1];
    }
}

編譯後執行時輸出
1D Array
1 1 1 1 1
Modify 1D Array
1 1 -1 1 1


2維 array


建立 2維 array 時需要使用雙重指標,再用 new 分配記憶體給 array,最後用 for 迴圈設定 array 的內容。呼叫自訂函式修改 array 內容時,要使用傳指標呼叫,不需要回傳值。
#include <iostream>
using namespace std;

void myfunc(int**);
void myprint(int**, int, int);

int main() {
    int n = 3, m = 5;
    int **data;  // 不能直接使用 int data[n][m]
    data = new int *[n];  // n 個指標物件,1維 array
    // 使用2層 for 迴圈填入元素值,data 內容為 {{1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}}
    for(int i=0; i<n; i++) {
    	data[i] = new int [m];  // m 個元素的1維 array
        for(int j=0; j<m; j++) {
            data[i][j] = 1;
        }
    }
    cout << "2D Array" << endl;
    myprint(data, n, m);
    cout << "Modify 2D Array" << endl;
    myfunc(data);  // 內容變為 {{1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, -1, 1, 1}}
    myprint(data, n, m);
    return 0;
}

/* 自訂函式,呼叫後將索引值為[2][2]的元素改為-1 */
void myfunc(int **a) {
    a[2][2] = -1;
}

/* 自訂函式,印出 array 內容,用空格分隔,印出每列最後一個元素時換行 */
void myprint(int **a, int n, int m) {
    for(int i=0; i<n; i++) {
        for(int j=0; j<m; j++) {
            cout << a[i][j] << " \n"[j == m-1];
        }
    }
}

編譯後執行時輸出
2D Array
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
Modify 2D Array
1 1 1 1 1
1 1 1 1 1
1 1 -1 1 1


修改 vector


1維 vector


依照一般的方式建立 vector 即可,呼叫自訂函式修改 vector 內容時,要使用傳參考呼叫,不需要回傳值。
#include <iostream>
#include <vector>
using namespace std;

void myfunc(vector<int>&);
void myprint(vector<int>);

int main() {
    int n = 5;
    vector<int> data (n, 1);  // 內容為 {1, 1, 1, 1, 1}
    cout << "1D Vector" << endl;
    myprint(data);
    cout << "Modify 1D Vector" << endl;
    myfunc(data);  // 內容變為 {1, 1, -1, 1, 1}
    myprint(data);
    return 0;
}

/* 自訂函式,呼叫後將索引值為2的元素改為-1 */
void myfunc(vector<int> &a) {
    a[2] = -1;
}

/* 自訂函式,印出 vector 內容,用空格分隔,印出最後一個元素時換行 */
void myprint(vector<int> a) {
    for(auto it = a.begin(); it != a.end(); it++) {
        cout << *it << " \n"[it == a.end()-1];
    }
}

編譯後執行時輸出
1D Vector
1 1 1 1 1
Modify 1D Vector
1 1 -1 1 1


2維 vector


依照一般的方式建立 vector 即可,呼叫自訂函式修改 vector 內容時,要使用傳參考呼叫,不需要回傳值。
#include <iostream>
#include <vector>
using namespace std;

void myfunc(vector<vector<int>>&);
void myprint(vector<vector<int>>);

int main() {
    int n = 3, m = 5;
    vector<vector<int>> data;  // 建立空的2維 vector
    // 使用 for 迴圈及 push_back 填入元素值,data 內容為 {{1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}}
    for(int i=0; i<n; i++) data.push_back(vector<int> (m, 1));
    cout << "2D Vector" << endl;
    myprint(data);
    cout << "Modify 2D Vector" << endl;
    myfunc(data);  // 內容變為 {{1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, -1, 1, 1}}
    myprint(data);
    return 0;
}

/* 自訂函式,呼叫後將索引值為[2][2]的元素改為-1 */
void myfunc(vector<vector<int>> &a) {
    a[2][2] = -1;
}

/* 自訂函式,印出 vector 內容,用空格分隔,印出每列最後一個元素時換行 */
void myprint(vector<vector<int>> a) {
    for(auto it = a.begin(); it != a.end(); it++) {
        for(auto it2 = (*it).begin(); it2 != (*it).end(); it2++) {
            cout << *it2 << " \n"[it2 == (*it).end()-1];
        }
    }
}

編譯後執行時輸出
2D Vector
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
Modify 2D Vector
1 1 1 1 1
1 1 1 1 1
1 1 -1 1 1


結語


以上的技巧在 APCS 實作題中偶爾會用到,例如111年10月APCS實作題第2題運貨站,我使用2維 array 儲存倉庫格子的資料,呼叫自訂函式檢查貨物是否能放入倉庫,如果可以放入倉庫則修改對應的格子狀態,需要透過自訂函式修改2維 array 的內容。希望這篇筆記能幫助到有需要的同學。



HackMD 版本連結:https://hackmd.io/@yizhewang/rJYbKxala

沒有留言:

張貼留言