当前位置: 首页 > news >正文

静态通讯录,适合初学者的手把手一条龙讲解

数据结构的顺序表和链表是一个比较困难的点,初见会让我们觉得有点困难,正巧C语言中有一个类似于顺序表和链表的小程序——通讯录。我们今天就来讲一讲通讯录的实现,也有利于之后顺序表和链表的学习。

目录

0.通讯录的初始化 

1.菜单的创建

2.主函数的创建

3.定义联系人内容(结构体)

4.定义通讯录内容(结构体)

5.避免代码冗余而定义的查找函数

6.删除联系人信息

7.修改通讯录信息

8.显示某一个人的信息

9.整个通讯录的打印

10.将通讯录按名字排序

11.全部代码

我们需要用C语言模拟一个通讯录可以用来存储100个人的信息

每个人的信息包括:

姓名、电话、性别、住址、年龄

功能包括:

  1. 新增联系人
  2. 查找联系人
  3. 删除联系人
  4. 修改联系人
  5. 查看所有联系人
  6. 以名字排序所有联系人 

0.通讯录的初始化 

尽管我们这是一个基础的通讯录,它并不具备保存功能,但是我们应该让它拥有一个初始化功能。

void InitContact(Contact* pc)
{pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}

1.菜单的创建

首先要有一个一目了然的菜单。

要实现这么多的功能,我们首先想到了switch语句,用case表示不同的情况。

不过考虑到case 1,case 2这样的写法不容易读懂,代码的可维护性也不高。

我们在这里定义一个枚举类型。

enum Option
{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};

 如图所示,枚举类型在我们没有额外操作的情况下,里面的元素是从零开始

这样一来,我们就可以从case 1这样的写法变成 case ADD,一目了然,一看就能看懂。

我们也需要打印选项,另外定义一个menu函数:

void menu()
{printf("'****************************************\n");printf("'**********   1.add     2.del   *********\n");printf("'**********   3.search  4.modify ********\n");printf("'**********   5.show    6.sort   ********\n");printf("'**********   0.exit             ********\n");printf("'****************************************\n");
}

2.主函数的创建

当要实现某一个函数功能时,我们直接在case语句后调用此函数便可。

int main()
{int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch(input){case ADD:break;case DEL:break;case SEARCH:break;case MODIFY:break;case SHOW:break;case SORT:break;case EXIT:printf("退出");break;default:break;}} while (input);return 0;
}

缺少的东西我们待会再补上。

3.定义联系人内容(结构体)

一个人的信息包括很多方面,要录入的话还是用结构体比较方便。

我们定义姓名,年龄,性别,电话和地址五个变量,并将其重命名为PeoInfo。

typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];
}PeoInfo;

4.定义通讯录内容(结构体)

typedef struct Contact
{PeoInfo data[MAX];int sz;
}Contact;

 我们要在什么位置插入一个元素呢?

我们知道,下标是从0开始的。

当我们插入一个数据时,我们实际上是插入在我们所定义的sz的位置。

举个例子。

当sz为5时,我们已经插入了下标为0,1,2,3,4的五个元素。

这时如果要插入新元素的话,就是在下标为5的地方插入,也就是插入到sz的位置。

但是我们并没有在sz的位置分配空间,所以我们在输入之后,需要将sz加一来分配空间。

void AddContact(Contact* pc)
{if (pc->sz == 100){printf("通讯录已满\n");return;}printf("请输入名字:>");scanf("%s", pc->data[pc->sz].name);printf("请输入年龄:>");scanf("%d", &pc->data[pc->sz].age);printf("请输入性别:>");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");
}

需要注意的是,当空间已满时,要返回,这时就不要往里面硬加了。

5.避免代码冗余而定义的查找函数

在接下来的修改或者删除函数里面,我们将会需要查找所要删除或者修改的人的名字这样的一个功能,为了避免冗余,我们不是在用到时再写一遍,而是定义一个函数,在用到它的时候直接调用,这就方便了许多。

int FindByName(Contact* pc, char name[])
{int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;break;}}return -1;
}

当查找到要查找到名字时,返回这个位置的下标,没有找到则返回-1。

6.删除联系人信息

要删除一个人的信息,首先需要输入这个人的名字并查找它的位置,这时上面的代码就派上用场了。

我们先调用函数找到这个人的信息,然后调用它的返回值,也就是目标联系人的下标,并用这个下标来修改。

修改的方式是经典的后一个覆盖前一个,不过由于下标是元素个数再减一,为了避免溢出,我们需要是将sz减一。

void DelContact(Contact* pc)
{char name[MAX_NAME];if (pc->sz == 0){printf("通讯录已满\n");return;}printf("输入要删除的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");}

同样的,在通讯录元素已满时,返回。

7.修改通讯录信息

首先查找,当查找到时,我们只需利用它的下标,来将此位置的信息进行重新录入即可。

void ModifyContact(Contact* pc)
{printf("输入要修改的人的名字:>");char name[12] = { 0 };scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人不存在\n");return;}printf("请输入名字:>");scanf("%s", pc->data[pos].name);printf("请输入年龄:>");scanf("%d", &pc->data[pos].age);printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");
}

8.显示某一个人的信息

我们调用上面的查找函数 ,找到就用返回值下标打印出来就行。

int SearchContact(Contact* pc)
{char name[12] = { 0 };printf("请输入要查找的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人信息不存在\n");} printf("此人信息如下:\n");printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);return pos;
}

9.整个通讯录的打印

我们调用sz,来将每一个人的信息打印出来。

void ShowContact(Contact* pc)
{int i = 0;printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->sz; i++){printf("%-10s %-4d %-5s %-12s %-30s\n",  pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}
}

10.将通讯录按名字排序

qsort函数可以很好的实现这个问题。

1.首元素地址base
我们要排序一组数据,首先我们需要找到这组数据在哪,因此我们直接将首元素的地址传给qsort函数来确定从哪开始排序。

2.元素个数num
我们知道了从哪开始,也要知道在哪结束才能确定一组需要排序的数据,但是我们不方便直接将结尾元素的地址传入函数,因此我们将需要排序的元素的个数传给qsort函数来确定一组数据。

3.元素大小size
我们知道qsort函数能排序任意数据类型的一组数据,因此我们用void*类型的指针来接收元素,但是我们知道void*类型的指针不能进行加减操作,也就无法移动,那么在函数内部我们究竟用什么类型的指针来操作变量呢?我们可以将void*类型的指针强制类型转换成char*类型的指针后来操作元素,因为char*类型的指针移动的单位字节长度是1个字节,我们只需要再知道我们需要操作的数据是几个字节就可以操作指针从一个元素移动到下一个元素,因此我们需要将元素大小传入qsort函数。

4.自定义比较函数compare

我们需要告诉qsort函数我们希望数据按照怎么的方式进行比较,比如对于几个字符串,我们可以比较字符串的大小。代码如下,我们想要传入什么类型的参数,就要将其强制类型转换成什么类型,并且返回一个int值。

int cmp_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}

然后就是qsort函数的调用了:

void SortContact(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
}

11.全部代码

下面是全部的代码,定义两个源文件和一个头文件。

//contact.h部分
#pragma once
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];
}PeoInfo;
typedef struct Contact
{PeoInfo data[MAX];int sz;
}Contact;
void InitContact(Contact* pc);
void AddContact(Contact* pc);
void DelContact(Contact* pc);
void SortContact(Contact* pc);
int SearchContact(Contact* pc);
void ModifyContact(Contact* pc); 
void ShowContact(Contact* pc);
int FindByName(Contact* pc, char name[]);
//contact.c
#include"contact.h"
void InitContact(Contact* pc)
{pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}
void AddContact(Contact* pc)
{if (pc->sz == 100){printf("通讯录已满\n");return;}printf("请输入名字:>");scanf("%s", pc->data[pc->sz].name);printf("请输入年龄:>");scanf("%d", &pc->data[pc->sz].age);printf("请输入性别:>");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");
}
void DelContact(Contact* pc)
{char name[MAX_NAME];if (pc->sz == 0){printf("通讯录已满\n");return;}printf("输入要删除的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}for (int i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");}
int FindByName(Contact* pc, char name[])
{int i = 0;for (i = 0; i < pc->sz; i++){if (strcmp(pc->data[i].name, name) == 0){return i;break;}}return -1;
}
int cmp_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}
int SearchContact(Contact* pc)
{char name[12] = { 0 };printf("请输入要查找的人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人信息不存在\n");} printf("此人信息如下:\n");printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr);return pos;
}
void ShowContact(Contact* pc)
{int i = 0;printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");for (i = 0; i < pc->sz; i++){printf("%-10s %-4d %-5s %-12s %-30s\n",  pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}
}
void ModifyContact(Contact* pc)
{printf("输入要修改的人的名字:>");char name[12] = { 0 };scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("此人不存在\n");return;}printf("请输入名字:>");scanf("%s", pc->data[pos].name);printf("请输入年龄:>");scanf("%d", &pc->data[pos].age);printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");
}
void SortContact(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
}
//test.c
#include"contact.h"
void menu()
{printf("'****************************************\n");printf("'**********   1.add     2.del   *********\n");printf("'**********   3.search  4.modify ********\n");printf("'**********   5.show    6.sort   ********\n");printf("'**********   0.exit             ********\n");printf("'****************************************\n");
}
enum Option
{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};
int main()
{int input = 0;Contact con;InitContact(&con);do{menu();printf("请选择:>");scanf("%d", &input);switch(input){case ADD:AddContact(&con);break;case DEL:DelContact(&con);break;case SEARCH:SearchContact(&con);break;case MODIFY:ModifyContact(&con);break;case SHOW:ShowContact(&con);break;case SORT:SortContact(&con);break;case EXIT:printf("退出");break;default:printf("输入错误\n");break;}} while (input);return 0;
}

这篇博客旨在总结我自己阶段性的学习,要是能帮助到大家,那可真是三生有幸!😀如果觉得我写的不错的话还请点个赞和关注哦~我会持续输出编程的知识的!🌞🌞🌞  

相关文章:

静态通讯录,适合初学者的手把手一条龙讲解

数据结构的顺序表和链表是一个比较困难的点&#xff0c;初见会让我们觉得有点困难&#xff0c;正巧C语言中有一个类似于顺序表和链表的小程序——通讯录。我们今天就来讲一讲通讯录的实现&#xff0c;也有利于之后顺序表和链表的学习。 目录 0.通讯录的初始化 1.菜单的创建…...

【你不知道的 CSS】你写的 CSS 太过冗余,以至于我对它下手了

:is() 你是否曾经写过下方这样冗余的CSS选择器: .active a, .active button, .active label {color: steelblue; }其实上面这段代码可以这样写&#xff1a; .active :is(a, button, label) {color: steelblue; }看~是不是简洁了很多&#xff01; 是的&#xff0c;你可以使用…...

Lesson 8.1 决策树的核心思想与建模流程

文章目录一、借助逻辑回归构建决策树1. 决策树实例2. 决策树知识补充2.1 决策树简单构建2.2 决策树的分类过程2.3 决策树模型本质2.4 决策树的树生长过程2.5 树模型的基本结构二、决策树的分类与流派1. ID3(Iterative Dichotomiser 3) 、C4.5、C5.0 决策树2. CART 决策树3. CHA…...

【算法】FIFO先来先淘汰算法分析和编码实战

背景 在设计一个系统的时候&#xff0c;由于数据库的读取速度远小于内存的读取速度 为加快读取速度&#xff0c;将一部分数据放到内存中称为缓存&#xff0c;但内存容量是有限的&#xff0c;当要缓存的数据超出容量&#xff0c;就需要删除部分数据 这时候需要设计一种淘汰机制…...

二分查找——我欲修仙(功法篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️我欲修仙】 学习名言&#xff1a;临渊羡鱼,不如退而结网——《汉书董仲舒传》 系列文章目录 第一章 ❤️ 二分查找 文章目录系列文章目录前言&#x1f697;&#x1f697;&#x1f697;二分查找&…...

Python 多线程

文章目录一、简介1.1 多线程的特性1.2 GIL二、线程1.2 单线程1.3 多线程三、线程池3.1 pool.submit3.2 pool.map四、Lock&#xff08;线程锁&#xff09;4.1 无锁导致的线程资源异常4.2 有锁五、Event&#xff08;事件&#xff09;5.1 简介5.2 示例六、Queue&#xff08;队列&a…...

JVM笔记(九)选择合适的垃圾收集器

Epsilon收集器Epsilon收集器由RedHat公司在JEP 318中提出&#xff0c;在此提案里Epsilon被形容成一个无操作的收集器&#xff08;A No-Op Garbage Collector&#xff09;&#xff0c;而事实上只要Java虚拟机能够工作&#xff0c;垃圾收集器便不可能是真正“无操作”的。原因是“…...

二维图像处理到三维点云处理

一、Opencv和PCL 下面是opencv和pcl的特点、区别和联系的详细对比表格。 特点/区别/联系OpenCVPCL英文全称Open Source Computer Vision LibraryPoint Cloud Library语言C、Python、JavaC功能图像处理(图像处理和分析、特征提取和描述、图像识别和分类、目标检测和跟踪等)、计…...

leetcode 删除有序数组中的重复项

题目 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。 由于在某些语言中不能改变数组的长度&#xff0c;所以必须将结果放在数组nums的第一…...

JVM学习.03 类加载机制

1、前言从事Java开发工作的都知道&#xff0c;Java程序提交到JVM运行时&#xff0c;需要编译成Class文件&#xff0c;才能被JVM加载运行。那么这些Class文件进入到虚拟机后会发生什么&#xff1f;以及Class是如何被加载的&#xff1f;这些都是本文要讲解的部分。2、类加载时机所…...

Celery使用:优秀的python异步任务框架

目录Celery 简介介绍安装基本使用Flask使用Celery异步任务定时任务Celery使用Flask上下文进阶使用参考停止Worker后台运行Celery 简介 介绍 Celery 是一个简单、灵活且可靠的&#xff0c;处理大量消息的分布式系统&#xff0c;并且提供维护这样一个系统的必需工具。 它是一个…...

第十四届蓝桥杯三月真题刷题训练——第 19 天

第 1 题&#xff1a;灌溉_BFS板子题 题目描述 小蓝负责花园的灌溉工作。 花园可以看成一个 n 行 m 列的方格图形。中间有一部分位置上安装有出水管。 小蓝可以控制一个按钮同时打开所有的出水管&#xff0c;打开时&#xff0c;有出水管的位置可以被认为已经灌溉好。 每经过一分…...

类和对象 - 下

本文已收录至《C语言》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言 正文 初始化列表 成员变量的定义与初始化 初始化列表的使用 变量定义顺序 explicit关键字 隐式类型转换 自定义类型隐式转换 explicit 限制转换 关于static static声明类成员 友元 友…...

【云原生】Linux基础IO(文件理解与操作)

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f38a;每篇一句&#xff1a; 图片来源 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 Great minds discuss ideas. Average minds discuss events. Small minds disc…...

CentOS 7 安装 mysql 8.0 客户端

只想安装 mysql-client 8.0 &#xff0c; 结果发现直接 yum install mysql mysql-client 安装的版本是 mysql Ver 15.1 Distrib 5.5.68-MariaDB &#xff0c;这个版本太低&#xff0c;连接其他服务器上的 mysql 8.0 时总是失败&#xff0c;因为 mysql 8.0 加密方式改变了&#…...

Ubuntu下载、配置、安装和编译opencv

1 安装相关依赖安装opencv前&#xff0c;需要先准备好编译器、相关依赖sudo apt-get install gcc g cmake vim sudo apt-get install build-essential libgtk2.0-dev libavcodec-dev libavformat-dev libjpeg-dev libswscale-dev libtiff5-dev sudo apt-get install libgtk2.0-…...

第七讲 贪心

文章目录股票买卖 II货仓选址&#xff08;贪心:排序中位数&#xff09;糖果传递&#xff08;❗贪心&#xff1a;中位数&#xff09;雷达设备&#xff08;贪心排序&#xff09;付账问题&#xff08;平均值排序❓&#xff09;乘积最大&#xff08;排序/双指针&#xff09;后缀表达…...

数字藏品的未来及发展趋势

随着互联网的普及以及数字文化的日益发展&#xff0c;数字藏品作为一种全新的收藏方式正在逐步兴起。数字藏品可以是数字版权、数字艺术品、数字音乐以及数字视频等形式&#xff0c;这些藏品通过数字化技术保存下来&#xff0c;并在互联网上进行传播和交易。数字藏品的发展趋势…...

值得记忆的STL常用算法,分分钟摆脱容器调用的困境,以vector为例,其余容器写法类似

STL常用算法 概述&#xff1a; 算法主要是由头文件<algorithm> <functional> <numeric>组成 <algorithm>是所有STL头文件中最大的一个&#xff0c;范围涉及到比较、交换、查找、遍历操作、复制、修改等等 <nuneric>体积很小&#xff0c;只包括…...

java如何手动导jar包

今天用IDEA&#xff0c;需要导入一个Jar包&#xff0c;因为以前都是用eclipse的&#xff0c;所以对这个idea还不怎么上手&#xff0c;连打个Jar包都是谷歌了一下。 但是发现网上谷歌到的做法一般都是去File –> Project Structure中去设置&#xff0c;有没有如同eclipse一样…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...