【C語言】回調函數、轉移表、qsort 使用與基于qsort改造冒泡排序

文章目錄

  • 數組指針/指針數組
  • 函數指針
  • 函數指針數組
    • 函數指針數組用途(轉移表)
  • 回調函數
  • qsort函數
  • 基于qsort改造冒泡排序
  • 源碼


數組指針/指針數組

	int arr1[5] = { 1,2,3,4,5 };int (*p1)[5] = &arr1;   //p1是數組指針變量int* arr2[5] = { 0 };   //arr2是指針數組

指針數組是存放指針的數組,本質是數組。
數組指針是指向數組的指針,本質是指針。
數組指針類型:(去掉變量名,剩下的就是類型)

int (*)[5]                  //數組指針類型

函數指針

函數名本身就是函數指針。

int Add(int x, int y)
{return x + y;
}int (*pf)(int, int) = &Add;   //pf就是存放函數地址(指針)的變量,函數指針變量

函數指針類型和數組指針類型相似,如下:

	int (*)(int, int)         //函數指針類型

通過函數指針調用函數:

int x = (*pf)(10, 20);
printf("%d\n", x);

因為函數名本身就是函數地址,所以前面的&Add 和 調用函數時的(*p) 中的取地址符和*不加也可以達到一樣的效果。

在這里插入圖片描述

函數指針數組

int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}int (*padd)(int, int) = &Add;
int (*psub)(int, int) = ⋐
int (*pmul)(int, int) = &Mul;
int (*pdiv)(int, int) = &Div;
// 類比 int arr[5]
int (*pfarr[4])(int, int) = { padd ,psub,pmul,pdiv };
//下面這樣這可以
int (*pfarr[4])(int, int) = { Add, Sub, Mul, Div };

上面的pfarr就是函數指針數組的變量名,函數指針數組最好在函數指針變量的基礎上改造得到,也就是在函數指針變量的變量名后面加[ ]。
函數指針數組的理解可以類比普通的數組,比如 int
arr[5],一個存放5個整型變量的數組,把它的變量名和中括號(arr[5])去掉剩余的就是數組存放變量的類型,放在函數指針數組里也一樣,把pfarr[4]去掉剩余的int(*)(int, int)就是數組里存放的變量類型。

函數指針數組用途(轉移表)

我們先來看下面實現的一個簡易計算器:

void menu()
{printf("********************\n");printf("*** 1.add  2.sub ***\n");printf("*** 3.mul  4.div ***\n");printf("***   0.exit     ***\n");printf("********************\n");
}int input = 0;
int a = 0;
int b = 0;
int r = 0;do{menu();printf("請選擇:");scanf("%d", &input);switch (input){case 1:printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = Add(a, b);printf("r = %d\n", r);break;case 2:printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = Sub(a, b);printf("r = %d\n", r);break;case 3:printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = Mul(a, b);printf("r = %d\n", r);break;case 4:printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = Div(a, b);printf("r = %d\n", r);break;case 0:printf("退出計算器\n");break;default:printf("選擇錯誤,重新選擇\n");break;}} while (input);

在這里插入圖片描述

我們可以看到雖然可以實現計算器的功能,但是代碼非常冗長,并且如果要增加更多運算函數的話還會增加更多的case,想要簡化代碼就可以用函數指針數組,把函數存放在函數指針數組里,想要調用函數直接下標訪問這個函數指針數組就行了,這里我們想要讓數組下標和菜單選擇數字對應的話就需要在數組開頭增加一個空指針,讓下標依次向后挪一位。

	int (*fparr[])(int, int) = {NULL,Add,Sub,Mul,Div};do{menu();printf("請選擇:");scanf("%d", &input);if (input > 0 && input <= 4){printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);//fparr[input]是數組下標訪問函數對象printf("r = %d\n", fparr[input](a, b)); }else if (input == 0){printf("退出計算器\n");break;}else{printf("選擇錯誤,重新選擇\n");}}while(input);

這樣的方法又名轉移表

回調函數

回調函數就是?個通過函數指針調?的函數。
如果你把函數的指針(地址)作為參數傳遞給另?個函數,當這個指針被?來調?其所指向的函數時,被調?的函數就是回調函數。回調函數不是由該函數的實現?直接調?,?是在特定的事件或條件發?時由另外的??調?的,?于對該事件或條件進?響應。
補充:至于回調函數為什么只能傳函數指針類型而不能直接傳遞函數類型,因為這是C語言語法特性決定的,就算傳遞的是函數類型,編譯器也會將它隱式轉化為函數指針類型。
下面我們舉個例子:

void calc(int (*pf)(int, int))
{int a = 0;int b = 0;int r = 0;printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = pf(a, b);printf("r = %d\n", r);
}int input = 0;do{menu();printf("請選擇:");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出計算器\n");break;default:printf("選擇錯誤,重新選擇\n");break;}} while (input);

之前我們實現的計算器switch-case部分非常冗余,有很多重復的代碼,case1-4的四行代碼只有調用運算函數那一行有區別,但是我們直接把代碼封裝成函數那又需要寫四個函數,所以這里就可以用回調函數。
首先寫一個回調函數calc,參數是運算函數對應的函數指針,所以每一個case只用去調用calc函數,根據不同的函數指針參數再在calc函數體中調用對應的運算函數。

在這里插入圖片描述

qsort函數

qsort函數是C語言的一個庫函數,它是基于快速排序算法實現的,這個函數可以用來排序任意類型數據。

在這里插入圖片描述

我們來分析一下它的四個參數:

在這里插入圖片描述

其中compar函數需要我們重點關注,這是我們自己設計的比較函數,C標準對這個函數是有約定的:

在這里插入圖片描述

compar函數返回值有三類,當參數p1指向的元素大于參數p2指向的元素時,返回大于0的數字,當參數p1指向的元素等于于參數p2指向的元素時,返回0,參數p1指向的元素小于參數p2指向的元素時,返回小于0的數字。
因為qsort默認是排升序的,所以我們按照上面的規定實現比較函數傳給qsort后最后結果為升序,如果要排降序就需要實現比較函數時把大小于號反號。
下面我們就來使用一下qsort,比較函數需要我們自己實現:

int cmp_int(const void* e1, const void* e2)
{//e1 e2 是void*,需要強制類型轉換后才能使用(解引用)return *(int*)e1 - *(int*)e2;
}void test02()
{int arr[] = { 4, 1, 2, 3, 6, 8, 7 };size_t sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}
}

比較結構體類型數據也是可以的:

typedef struct stu
{char name[20];size_t age;
}stu;cmp_struct_by_name(const void* e1, const void* e2)
{return strcmp(((stu*)e1)->name, ((stu*)e2)->name);
}cmp_struct_by_age(const void* e1, const void* e2)
{return ((stu*)e1)->age - ((stu*)e2)->age;
}void test03()
{stu arr[] = { {"jiyi", 7}, {"xiaoba", 8}, {"wusaqi", 6}, };size_t sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_struct_by_age);
}

按名字比較時需要調用strcmp函數,我們查文檔可以看到strcmp函數的返回值正好和我們要實現的比較函數邏輯一致,所以比較函數直接返回strcmp函數的返回值就行了。

在這里插入圖片描述

基于qsort改造冒泡排序

我們以前實現的冒泡排序只能排整型數據,我們想讓它適配更多類型的數據,就需要對它進行改造,這里模擬qsort的方式對我們自己的冒泡排序進行改造,我們先分析需要改造哪些地方:

在這里插入圖片描述

一、首先是參數,我們要比較不同類型數據就需要按照qsort的參數實現方式進行傳遞。
第一個參數傳遞數組首元素的地址,有了地址才能知道待排序數據在哪里和第一個元素的地址,方便以第一個元素的地址為基準指針加數字訪問到數組后面的元素(要配合第三個參數使用,因為第一個參數的類型的void*,無法直接加減數字操作,也無法確定強制類型轉換的類型)。
第二個參數傳遞數組的元素個數,用于確定循環躺數。
第三個參數傳遞單個元素的單位大小,因為無法直接傳遞參數類型,所以傳遞單個元素的單位大小來間接確定元素類型。因為首元素地址是void*,無法直接以首元素地址為基準指針加數字訪問到數組后面的元素,這里有一個思路就是將首元素地址強轉成char*,用它加單位數乘以第三個參數大小就可以訪問到單位位置的元素,比如(int*)base + j * (width) 就可以訪問到指向int數組的第j個元素的指針,然后再把指針作為參數傳給對應的比較函數,在比較函數內部再對指針解引用訪問數據大小并比較。
第四個參數是我們自己實現的比較函數。

二、然后是比較函數部分傳遞我們自己實現的比較函數。

三、最后是swap,因為不知道數據的類型,所以不能直接交換,在前面參數部分已經將首元素指針類型強轉成了char*,不如將計就計,在交換部分還是以char類型來交換,不管你的數據類型大小有多少字節,以char類型形式一個字節一個字節的交換,一共交換width次。

int cmp_int(const void* e1, const void* e2)
{//e1 e2 是void*,需要強制類型轉換后才能使用 return *(int*)e1 - *(int*)e2;
}swap(char* p1, char* p2, int width)
{for (width; width > 0; width--){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}Bubble_sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{int flag = 1;  //默認有序for (int i = 0; i < sz - 1; i++)  //躺數{for (int j = 0; j < sz - 1 - i; j++){//(char*)arr + j * widthif (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){swap((char*)base + j * width, (char*)base + (j + 1) * width, width);flag = 0;}}if (flag == 1)break;}
}print(int* arr , int sz)
{for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}void test04()
{int arr[] = { 4, 1, 2, 3, 6, 8, 7 };size_t sz = sizeof(arr) / sizeof(arr[0]);Bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print(arr, sz);
}

源碼

p1:

#include <stdio.h>int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}
void test01()
{int arr1[5] = { 1,2,3,4,5 };int (*p1)[5] = &arr1;         //p1是數組指針變量//int (*)[5]                  //數組指針類型int* arr2[5] = { 0 };         //arr2是指針數組int (*pf)(int, int) = &Add;   //pf就是存放函數地址(指針)的變量,函數指針變量//int (*)(int, int)           //函數指針類型int x = (*pf)(10, 20);printf("%d\n", x);
}void test02()
{int (*padd)(int, int) = &Add;int (*psub)(int, int) = &Sub;int (*pmul)(int, int) = &Mul;int (*pdiv)(int, int) = &Div;// 類比 int arr[5]int (*pfarr[4])(int, int) = { Add, Sub, Mul, Div };
}void menu()
{printf("********************\n");printf("*** 1.add  2.sub ***\n");printf("*** 3.mul  4.div ***\n");printf("***   0.exit     ***\n");printf("********************\n");
}void test03()
{int input = 0;int a = 0;int b = 0;int r = 0;do{menu();printf("請選擇:");scanf("%d", &input);switch(input){case 1:printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = Add(a, b);printf("r = %d\n", r);break;case 2:printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = Sub(a, b);printf("r = %d\n", r);break;case 3:printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = Mul(a, b);printf("r = %d\n", r);break;case 4:printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = Div(a, b);printf("r = %d\n", r);break;case 0:printf("退出計算器\n");break;default:printf("選擇錯誤,重新選擇\n");break;}} while (input);
}void test04()
{int input = 0;int a = 0;int b = 0;int r = 0;int (*fparr[])(int, int) = {NULL,Add,Sub,Mul,Div};do{menu();printf("請選擇:");scanf("%d", &input);if (input > 0 && input <= 4){printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);printf("r = %d\n", fparr[input](a, b));}else if (input == 0){printf("退出計算器\n");break;}else{printf("選擇錯誤,重新選擇\n");}}while(input);//do//{//	menu();//	printf("請選擇:");//	scanf("%d", &input);//	switch (input)//	{//	case 1://		printf("請輸入兩個操作數:");//		scanf("%d %d", &a, &b);//		r = Add(a, b);//		printf("r = %d\n", r);//		break;//	case 2://		printf("請輸入兩個操作數:");//		scanf("%d %d", &a, &b);//		r = Sub(a, b);//		printf("r = %d\n", r);//		break;//	case 3://		printf("請輸入兩個操作數:");//		scanf("%d %d", &a, &b);//		r = Mul(a, b);//		printf("r = %d\n", r);//		break;//	case 4://		printf("請輸入兩個操作數:");//		scanf("%d %d", &a, &b);//		r = Div(a, b);//		printf("r = %d\n", r);//		break;//	case 0://		printf("退出計算器\n");//		break;//	default://		printf("選擇錯誤,重新選擇\n");//		break;//	}//} while (input);
}void calc(int (*pf)(int, int))
{int a = 0;int b = 0;int r = 0;printf("請輸入兩個操作數:");scanf("%d %d", &a, &b);r = pf(a, b);printf("r = %d\n", r);
}void test05()
{int input = 0;do{menu();printf("請選擇:");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出計算器\n");break;default:printf("選擇錯誤,重新選擇\n");break;}} while (input);
}int main()
{//test01();//test02();//test03();//test04();test05();return 0;
}

p2:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>//Bubble_sort(int arr[], int sz)
//{
//    int flag = 1;  //默認有序
//    for (int i = 0; i < sz - 1; i++)  //躺數
//    {
//        for (int j = 0; j < sz - 1 - i; j++)
//        {
//            if (arr[j] > arr[j + 1])
//            {
//                int tmp = arr[j];
//                arr[j] = arr[j + 1];
//                arr[j + 1] = tmp;
//                flag = 0;
//            }
//
//        }
//        if (flag == 1)
//            break;
//    }
//}
//test01()
//{
//    int arr[] = { 4, 1, 2, 3, 6, 8, 7 };
//    Bubble_sort(arr, 7);
//    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//    {
//        printf("%d ", arr[i]);
//    }
//}int cmp_int(const void* e1, const void* e2)
{//e1 e2 是void*,需要強制類型轉換后才能使用 return *(int*)e1 - *(int*)e2;
}//print(int* arr)
//{
//    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//        {
//            printf("%d ", arr[i]);
//        }
//}//void test02()
//{
//    int arr[] = { 4, 1, 2, 3, 6, 8, 7 };
//    size_t sz = sizeof(arr) / sizeof(arr[0]);
//    qsort(arr, sz, sizeof(arr[0]), cmp_int);
//
//    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//    {
//        printf("%d ", arr[i]);
//    }
//}typedef struct stu
{char name[20];size_t age;
}stu;cmp_struct_by_name(const void* e1, const void* e2)
{return strcmp(((stu*)e1)->name, ((stu*)e2)->name);
}cmp_struct_by_age(const void* e1, const void* e2)
{return ((stu*)e1)->age - ((stu*)e2)->age;
}//void test03()
//{
//    stu arr[] = { {"jiyi", 7}, {"xiaoba", 8}, {"wusaqi", 6}, };
//    size_t sz = sizeof(arr) / sizeof(arr[0]);
//    qsort(arr, sz, sizeof(arr[0]), cmp_struct_by_age);
//}swap(char* p1, char* p2, int width)
{for (width; width > 0; width--){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}Bubble_sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{int flag = 1;  //默認有序for (int i = 0; i < sz - 1; i++)  //躺數{for (int j = 0; j < sz - 1 - i; j++){//(char*)arr + j * widthif (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){swap((char*)base + j * width, (char*)base + (j + 1) * width, width);flag = 0;}}if (flag == 1)break;}
}//void test_func(void* p1)
//{
//    p1[1];
//}print(int* arr , int sz)
{for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}void test04()
{int arr[] = { 4, 1, 2, 3, 6, 8, 7 };size_t sz = sizeof(arr) / sizeof(arr[0]);Bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print(arr, sz);
}int main()
{//test01();//test02();//test03();test04();return 0;
}

以上就是小編分享的全部內容了,如果覺得不錯還請留下免費的關注和收藏 如果有建議歡迎通過評論區或私信留言,感謝您的大力支持。
一鍵三連好運連連哦~~

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914164.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914164.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914164.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue3 uniapp 使用ref更新值后子組件沒有更新 ref reactive的區別?使用from from -item執行表單驗證一直提示沒有值

遇到這樣一個問題&#xff0c;我有個1個頁面A&#xff0c;一個from表單組件&#xff0c;一個form-item組件&#xff0c; 使用是這樣的&#xff0c;我在父組件A中使用 &#xff0c;執行表單驗證一直提示沒有值咱們先來講一講ref 和reactive的區別 ref 用來創建一個基本類型或單…

PyQt5布局管理(QBoxLayout(框布局))

QBoxLayout&#xff08;框布局&#xff09; 采用QBoxLayout類可以在水平和垂直方向上排列控件&#xff0c;QHBoxLayout和 QVBoxLayout類繼承自QBoxLayout類。 QHBoxLayout&#xff08;水平布局&#xff09; 采用QHBoxLayout類&#xff0c;按照從左到右的順序來添加控件。QHBoxL…

Grok 4作戰圖刷爆全網,80%華人橫掃硅谷!清華上交校友領銜,95后站C位

來源 | 新智元短短兩年&#xff0c;馬斯克Grok 4的橫空出世&#xff0c;讓xAI團隊一舉站上AI之巔。昨日一小時發布會&#xff0c;Grok 4讓所有人大開眼界&#xff0c;直接刷爆了AIME 2025、人類最后的考試&#xff08;HLE&#xff09;兩大基準。這是狂堆20萬GPU才換來的驚人成果…

AI大模型(七)Langchain核心模塊與實戰(二)

Langchain核心模塊與實戰&#xff08;二&#xff09;Langchian向量數據庫檢索Langchian構建向量數據庫和檢索器批量搜索返回與之相似度最高的第一個檢索器和模型結合得到非籠統的答案LangChain構建代理通過代理去調用Langchain構建RAG的對話應用包含歷史記錄的對話生成Langchia…

Flutter基礎(前端教程①-容器和控件位置)

一個紅色背景的 Container垂直排列的 Column 布局中央的 ElevatedButton按鈕下方的白色文本import package:flutter/material.dart;void main() {runApp(const MyApp()); }class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);overrideWidget bu…

CSS flex

目錄 flex-box和flex-item 主軸和副軸 ?編輯 flex-box的屬性 flex-direction flex-wrap flex-flow justify-content ?編輯?align-items align-content flex-item的屬性 flex-basis flex-grow flex-shrink flex flex-box和flex-item 當把一個塊級元素的displ…

【JMeter】執行系統命令

步驟如下&#xff1a; 添加JSP233 Sampler&#xff1a;右擊線程組>添加>取樣器>JSR223 Sampler2.填寫腳本&#xff0c;執行后查看日志。res "ipconfig".execute().text log.info(res)res "python -c \"print(11)\"".execute().text l…

AI Agent開發學習系列 - langchain之memory(1):內存中的短時記憶

內存中的短時記憶&#xff0c;在 LangChain 中通常指 ConversationBufferMemory 這類“對話緩沖記憶”工具。它的作用是&#xff1a;在內存中保存最近的對話歷史&#xff0c;讓大模型能理解上下文&#xff0c;實現連續對話。 對話緩沖記憶”工具 主要特點 只保留最近的對話內容…

uniapp實現微信小程序端圖片保存到相冊

效果圖展示 安裝插件海報畫板導入到項目里面&#xff0c;在頁面直接使用 <template><view><button click"saveToAlbum" class"save-button">保存到相冊</button><image :src"path" mode"widthFix" v-if&qu…

Java生產帶文字、帶邊框的二維碼

Java 生成帶文字、帶邊框的二維碼1、Java 生成帶文字的二維碼1.1、導入jar包1.2、普通單一的二維碼1.2.1、代碼示例1.2.2、效果1.3、帶文字的二維碼1.&#xff13;.&#xff11;、代碼示例1.3.2、效果2、帶邊框的二維碼2.1、代碼示例2.2、帶邊框的二維碼效果 1、Java 生成帶文字…

ARM單片機啟動流程(三)(棧空間綜合理解及相關實際應用)

文章目錄1、引出棧空間問題2、解決問題2.1、RAM空間2.2、RAM空間具體分布2.3、關于棧空間的使用2.4、棧溢出2.5、變量的消亡2.6、回到關鍵字static2.7、合法性的判斷1、引出棧空間問題 從static關鍵字引出該部分內容。 為什么能從static引出來&#xff1f; 在使用該關鍵字的…

【RK3568+PG2L50H開發板實驗例程】FPGA部分 | 鍵控LED實驗

本原創文章由深圳市小眼睛科技有限公司創作&#xff0c;版權歸本公司所有&#xff0c;如需轉載&#xff0c;需授權并注明出處&#xff08;www.meyesemi.com) 1.實驗簡介 實驗目的&#xff1a; 從創建工程到編寫代碼&#xff0c;完成引腳約束&#xff0c;最后生成 bit 流下載到…

【Python練習】039. 編寫一個函數,反轉一個單鏈表

039. 編寫一個函數,反轉一個單鏈表 039. 編寫一個函數,反轉一個單鏈表方法 1:迭代實現運行結果代碼解釋方法 2:遞歸實現運行結果代碼解釋選擇方法迭代法與遞歸法的區別039. 編寫一個函數,反轉一個單鏈表 在 Python 中,可以通過迭代或遞歸的方式反轉一個單鏈表。 方法 1…

BERT代碼簡單筆記

參考視頻&#xff1a;BERT代碼(源碼)從零解讀【Pytorch-手把手教你從零實現一個BERT源碼模型】_嗶哩嗶哩_bilibili 一、BertTokenizer BertTokenizer 是基于 WordPiece 算法的 BERT 分詞器&#xff0c;繼承自 PreTrainedTokenizer。 繼承的PretrainedTokenizer&#xff0c;具…

PID控制算法理論學習基礎——單級PID控制

這是一篇我在學習PID控制算法的過程中的學習記錄。在一開始學習PID的時候&#xff0c;我也看了市面上許多的資料&#xff0c;好的資料固然有&#xff0c;但是更多的是不知所云。&#xff08;有的是寫的太過深奧&#xff0c;有的則是照搬挪用&#xff0c;對原理則一問三不知&…

【Elasticsearch】function_score與rescore

它們倆都是用來“**干涉評分**”的&#xff0c;但**工作階段不同、性能開銷不同、能做的事也不同**。一句話總結&#xff1a;> **function_score** 在 **第一次算分** 時就動手腳&#xff1b; > **rescore** 在 **拿到 Top-N 結果后** 再“重新打分”。下面把“能干嘛”…

無廣告純凈體驗 WPS2016 精簡版:移除聯網模塊 + 非核心組件,古董電腦也能跑

各位辦公小能手們&#xff01;今天給你們介紹一款超神的辦公軟件——WPS2016精簡版&#xff01;它有多小呢&#xff1f;才33MB&#xff0c;簡直就是軟件界的小不點兒&#xff01;別看它個頭小&#xff0c;功能可一點兒都不含糊&#xff0c;文字、表格、演示這三大功能它全都有。…

《PyWin32:Python與Windows的橋梁,解鎖系統自動化新姿勢》

什么是 PyWin32在 Windows 平臺的 Python 開發領域中&#xff0c;PyWin32 是一個舉足輕重的庫&#xff0c;它為 Python 開發者打開了一扇直接通往 Windows 操作系統底層功能的大門。簡單來說&#xff0c;PyWin32 是用于 Python 訪問 Windows API&#xff08;Application Progra…

vite如何生成gzip,并在服務器上如何設置開啟

1. 安裝插件npm install vite-plugin-compression -D2. 在 vite.config.ts 中配置TypeScriptimport { defineConfig } from vite import compression from vite-plugin-compressionexport default defineConfig({plugins: [compression({algorithm: gzip,ext: .gz,threshold: 1…

1068萬預算!中國足協大模型項目招標,用AI技術驅動足球革命

中國足協啟動國際足聯“前進計劃”下的大數據模型項目&#xff0c;預算1068萬元。該項目將建立足球大數據分析平臺&#xff0c;利用AI技術為國家隊、青少年足球、業余球員及教練員裁判員提供精準數據分析服務&#xff0c;旨在通過科技手段提升中國足球競技水平。 中國足球迎來數…