基于Linux的云端垃圾分类助手

项目简介

本项目旨在开发一个基于嵌入式系统的智能垃圾分类装置。该装置能够通过串口通信、语音播报、网络通信等多种方式,实现垃圾的自动识别和分类投放。系统采用多线程设计,确保各功能模块高效并行工作。

项目功能

  1. 垃圾分类识别

系统使用摄像头拍摄垃圾图片,并将其上传到阿里云进行分类识别。
识别结果包括干垃圾、湿垃圾、可回收垃圾、有害垃圾等类别。

  1. 语言播报

系统通过串口与语言模块通信,根据垃圾分类结果进行语音播报。
语音模块接收指令并播放相应的提示音。

  1. OLED显示

系统通过OLED屏幕显示当前垃圾分类结果。

4.垃圾桶盖控制

系统根据识别结果控制相应垃圾桶的开关。
使用PWM信号控制舵机开关垃圾桶盖。

5.网络通信

系统支持通过网络接收客户端指令,实现远程控制。
通过TCP/IP协议接收来自客户端的“open”指令,触发垃圾分类操作。

硬件需求

Orange Pi Zero2
摄像头模块
语音播报模块SU-03T
128*64 OLED显示屏
SG90舵机
串口通信模块

运行环境

操作系统:Linux
编程语言:C语言、Python

功能实现

garbage.py

调用阿里云SDK API

# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_imagerecog20190930
//引入必要的Python包,包括阿里云图像识别服务的SDK包。
import os
import io
from urllib.request import urlopen
from alibabacloud_imagerecog20190930.client import Client
from alibabacloud_imagerecog20190930.models import ClassifyingRubbishAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions//通过读取环境变量中的AccessKey ID和AccessKey Secret,配置阿里云客户端的认证信息和访问域名。
config = Config(# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),# 访问的域名endpoint='imagerecog.cn-shanghai.aliyuncs.com',# 访问的域名对应的regionregion_id='cn-shanghai'
)//垃圾分类函数,提供了从本地文件读取和从URL读取,两种方式
def alibaba_garbage():#场景一:文件在本地img = open(r'/tmp/garbage.jpg', 'rb') //打开文件所在路径#场景二:使用任意可访问的url#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/imagerecog/ClassifyingRubbish/ClassifyingRubbish1.jpg'#img = io.BytesIO(urlopen(url).read())classifying_rubbish_request = ClassifyingRubbishAdvanceRequest()classifying_rubbish_request.image_urlobject = imgruntime = RuntimeOptions()try:# 初始化Clientclient = Client(config)//调用阿里云图像识别服务,将上传的垃圾图片进行分类,并返回分类结果。response = client.classifying_rubbish_advance(classifying_rubbish_request, runtime)# 获取整体结果print(response.body.to_map()['Data']['Elements'][0]['Category'])return response.body.to_map()['Data']['Elements'][0]['Category']except Exception as error:return 'gat failed'在脚本被直接执行时,调用‘alibaba_garbage’函数。
if __name__ == "__main__":alibaba_garbage()

garbage.c

在C语言中嵌入Python解释器以调用Python函数。

#include <Python.h>
#include "garbage.h"void garbage_init(void)
{	//初始化Python解释器Py_Initialize();//导入‘sys’模块PyObject *sys = PyImport_ImportModule("sys");//获取‘sys.path’模块PyObject *path = PyObject_GetAttrString(sys, "path");//将当前目录‘.’添加到'sys.path'中,以便导入当前目录下的Python脚本。PyList_Append(path, PyUnicode_FromString("."));
}void garbage_final(void)
{//终止Python解释器。	Py_Finalize();
}char *garbage_category(char *category)
{//导入名为‘garbage.py’的python模块。//如果导入失败,打印错误信息并跳转到'FAILED_MODULE'表签进行清理。PyObject *pModule = PyImport_ImportModule("garbage");if(!pModule){PyErr_Print();printf("ERROR: faild to load garbage.py\n");goto FAILED_MODULE;}//获取'garbage'模块中的'alibaba_garbage'函数。//如果获取失败或函数不可调用,打印错误信息并跳转到 FAILED_FUNC 标签进行清理。PyObject *pFunc = PyObject_GetAttrString(pModule, "alibaba_garbage");if(!pFunc || !PyCallable_Check(pFunc)){PyErr_Print();printf("ERROR: function alibaba_garbage not found or not callable\n");goto FAILED_FUNC;}//调用 alibaba_garbage 函数,不传递参数。//如果调用失败,打印错误信息并跳转到 FAILED_VALUE 标签进行清理。PyObject *pValue = PyObject_CallObject(pFunc, NULL);if(!pValue){PyErr_Print();printf("ERROR: function call failed\n");goto FAILED_VALUE;}char *result = NULL;if(!PyArg_Parse(pValue, "s", &result)){PyErr_Print();printf("ERROR: parse failed\n");goto FAILED_RESULT;}category = (char *)malloc(sizeof(char *)*(strlen(result)+1));memset(category, 0 , (strlen(result)+1));strncpy(category, result, (strlen(result)+1));// 释放发生错误之前分配的内存空间
FAILED_RESULT:Py_DECREF(pValue);
FAILED_VALUE:Py_DECREF(pFunc);
FAILED_FUNC:Py_DECREF(pModule);
FAILED_MODULE:return category;
}

内存泄漏

内存泄漏是指在计算机程序中,动态分配的内存未被正确释放,导致这些内存块不能被重新分配和使用。随着程序的运行,未释放的内存不断累积,最终可能耗尽可用内存资源,导致系统性能下降,甚至使程序或系统崩溃。

内存泄漏的成因
  1. 未释放动态分配的内存:

程序在使用 malloc、calloc 或 realloc 分配内存后,没有使用 free 函数释放内存。

  1. 循环或递归调用中未释放内存:

在循环或递归调用中,动态分配的内存未被释放。

  1. 异常退出或提前返回:

程序在处理错误或异常时,没有正确释放已经分配的内存。

  1. 丢失指针引用:

动态分配的内存地址被覆盖或丢失,没有其他指针指向该内存块。

栈上分配和堆上分配
栈上分配

栈上分配的内存(例如局部变量)在函数调用结束后会自动释放。

void function() {int local_variable = 10; // 栈上分配// local_variable 在函数结束时自动释放
}
堆上分配

堆上分配的内存需要程序员手动释放,否则会导致内存泄漏。

void function() {int *heap_variable = (int *)malloc(sizeof(int) * 10); // 堆上分配// 使用完 heap_variable 后需要手动释放内存free(heap_variable);
}

garbage.h

#ifndef __GARBAGE__H
#define __GARBAGE__Hvoid garbage_init(void);
void garbage_final(void);
char *garbage_category(char *category);//宏定义拍照指令和图片地址
#define WGET_CMD "wget http://127.0.0.1:8080/?action=snapshot -O /tmp/garbage.jpg"
#define GARBAGE_FILE "/tmp/garbage.jpg"#endif

main.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
#include <pthread.h>#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
#include "myoled.h"
#include "socket.h"int serial_fd = -1;
pthread_cond_t cond;
pthread_mutex_t mutex;//该函数通过执行 shell 命令 ps 检测特定进程是否正在运行。
static int detect_process(const char *process_name)
{int n = -1;FILE *strm;char buf[128] = {0};sprintf(buf, "ps -ax | grep %s | grep -v grep", process_name);if ((strm = popen(buf, "r")) != NULL) {if (fgets(buf, sizeof(buf), strm) != NULL) {n = atoi(buf);}}else{return -1;}pclose(strm);return n;
}//从串口读取语音命令,并根据特定条件触发条件变量信号。
void *pget_voice(void *arg)
{unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA};int len = 0;if (-1 == serial_fd) {printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);pthread_exit(0);}printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);while(1){len = serialGetstring(serial_fd, buffer);if (len > 0 && buffer[2] == 0x46) {pthread_mutex_lock(&mutex);buffer[2] = 0x00;pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);}}pthread_exit(0);
}//发送语音命令到串口。
void *psend_voice(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer = (unsigned char *)arg;if (-1 == serial_fd){printf("%s|%s|%d: open serial failed\n", __FILE__, __func__, __LINE__);pthread_exit(0);}if (NULL != buffer){serialSendstring(serial_fd, buffer, 6);}pthread_exit(0);
}//控制垃圾桶打开和关闭。
void *popen_trash_can(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer = (unsigned char *)arg;if (buffer[2] == 0x43) {printf("%s|%s|%d: buffer[2]=0x%x\n", __FILE__, __func__, __LINE__, buffer[2]);pwm_write(PWM_RECOVERABLE_GARBAGE);delay(2000);pwm_stop(PWM_RECOVERABLE_GARBAGE);}else if (buffer[2] != 0x45){printf("%s|%s|%d: buffer[2]=0x%x\n", __FILE__, __func__, __LINE__, buffer[2]);pwm_write(PWM_GARBAGE);delay(2000);pwm_stop(PWM_GARBAGE);}pthread_exit(0);
}//初始化 OLED 显示屏并显示垃圾分类结果。
void*poled_show(void *arg)
{pthread_detach(pthread_self());myoled_init();oled_show(arg);pthread_exit(0);
}//等待条件变量信号,执行垃圾分类逻辑,并触发语音、垃圾桶和 OLED 显示。
void *pcategory(void *arg)
{unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA};char *category = NULL;pthread_t send_voice_tid, trash_tid, oled_tid;while(1){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond, &mutex);pthread_mutex_unlock(&mutex);buffer[2] = 0x00;system(WGET_CMD);if (0 == access(GARBAGE_FILE, F_OK)){category = garbage_category(category);if (strstr(category, "干垃圾")){buffer[2] = 0x41;}else if (strstr(category, "湿垃圾")){buffer[2] = 0x42;}else if (strstr(category, "可回收垃圾")){buffer[2] = 0x43;}else if (strstr(category, "有害垃圾")){buffer[2] = 0x44;}else {buffer[2] = 0x45;}}else{buffer[2] = 0x45;}//开语音播报线程pthread_create(&trash_tid, NULL, psend_voice, (void *)buffer);//开垃圾桶开关pthread_create(&send_voice_tid, NULL, popen_trash_can, (void *)buffer);//oled显示线程pthread_create(&oled_tid, NULL, poled_show, (void *)buffer);remove(GARBAGE_FILE);}}void *pget_socket(void *arg)
{int s_fd = -1;// 服务器套接字文件描述符int c_fd = -1;// 客户端套接字文件描述符char buffer[6];// 接收数据缓冲区int nread = -1;// 接收到的数据长度struct sockaddr_in c_addr;// 客户端地址结构memset(&c_addr,0,sizeof(struct sockaddr_in));// 初始化客户端地址结构s_fd = socket_init(IPADDR, IPPORT);// 初始化服务器套接字printf("%s|%s|%d:s_fd=%d\n", __FILE__, __func__, __LINE__, s_fd);if (-1 == s_fd){pthread_exit(0);}sleep(3);int clen = sizeof(struct sockaddr_in);// 客户端地址结构的长度while(1){c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);// 接受客户端连接int keepalive = 1; // 开启TCP KeepAlive功能int keepidle = 5; // 5s内没收到数据开始发送心跳包int keepcnt = 3; // 每次发送心跳包的次数int keepintvl = 3; // 每3s发送一次心跳包setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));setsockopt(c_fd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof(keepidle));setsockopt(c_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof(keepcnt));setsockopt(c_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof(keepintvl));        printf("%s|%s|%d: Accept a connection from %s:%d\n", __FILE__, __func__, __LINE__, inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));if(c_fd == -1){perror("accept");continue;}while(1){memset(buffer, 0, sizeof(buffer));nread = recv(c_fd, buffer, sizeof(buffer), 0); //n_read = read(c_fd,buffer, sizeof(buffer));printf("%s|%s|%d:nread=%d, buffer=%s\n", __FILE__, __func__,__LINE__, nread, buffer);if (nread > 0){if (strstr(buffer, "open")){pthread_mutex_lock(&mutex);pthread_cond_signal(&cond);pthread_mutex_unlock(&mutex);}}else if(0 == nread || -1 == nread){break;}}close(c_fd);}pthread_exit(0);
}int main(int argc, char *argv[])
{   int len = 0;int ret = -1;char *category = NULL;pthread_t get_voice_tid, category_tid, get_socket_tid;wiringPiSetup();garbage_init();ret = detect_process("mjpg_streamer"); //判断mjpg_streamer是否已经在运行if (-1 == ret){goto END;}else{printf("已运行mjpg_streamer,可以开始垃圾分类识别\n");}serial_fd = myserialOpen(SERIAL_DEV, BAUD);if (-1 == serial_fd) {goto END;}//开语音线程printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_create(&get_voice_tid, NULL, pget_voice, NULL);//开网络线程pthread_create(&get_socket_tid, NULL, pget_socket, NULL);//开阿里云交互线程printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);pthread_create(&category_tid, NULL, pcategory, NULL);pthread_join(get_voice_tid, NULL);pthread_join(category_tid, NULL);pthread_join(get_socket_tid, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);close(serial_fd);
END:garbage_final;return 0; 
}

关于TCP KeepAlive

通过设置 TCP KeepAlive 选项,可以在客户端异常断开连接时检测到失效连接,并在必要时关闭它。它会自动发生在操作系统内核层面。

  1. KeepAlive 探测: 当启用了 TCP KeepAlive 并设置了相关参数后,操作系统内核会在连接空闲指定时间(TCP_KEEPIDLE)后开始发送 KeepAlive 探测包。
  2. 检测响应: 内核会等待客户端对探测包的响应。如果在指定的次数(TCP_KEEPCNT)内没有收到客户端的响应,内核会认为该连接已经失效。
  3. 关闭连接: 当探测包没有得到响应时,内核会自动关闭该连接,并通知应用程序。此时,你在应用程序中的 recv 调用会返回 -1,表示连接已经断开。

oled.c

这段代码实现了一个 OLED 显示功能,用于在一个指定位置上显示垃圾分类的结果。具体来说,代码分为两个主要部分:oled_show 函数和 myoled_init 函数。

#include <error.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>#include "oled.h"
#include "font.h"
#include "myoled.h"#define FILENAME "/dev/i2c-3"static struct display_info disp;int oled_show(void *arg)
{unsigned char *buffer = (unsigned char *)arg;//在屏幕上的指定位置显示指定内容oled_putstrto(&disp, 0, 9+1, "This garbage is:");// 设置字体disp.font = font2;//根据 buffer[2] 的值显示相应的垃圾分类结果switch(buffer[2]){case 0x41:oled_putstrto(&disp, 0, 20, "dry waste");break;case 0x42:oled_putstrto(&disp, 0, 20, "wet waste");break;case 0x43:oled_putstrto(&disp, 0, 20, "recyclable waste");break;case 0x44:oled_putstrto(&disp, 0, 20, "hazardous waste");break;case 0x45:oled_putstrto(&disp, 0, 20, "recognition failed");break;}// 再次设置字体disp.font = font2;//把需要显示的内容刷新到屏幕上oled_send_buffer(&disp); return 0;
}int myoled_init(void)
{int e;// 设置显示屏地址和字体disp.address = OLED_I2C_ADDR;disp.font = font2;// 打开和初始化 OLED 显示屏e = oled_open(&disp, FILENAME);e = oled_init(&disp);return e;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/38901.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解密tar文件解压的Java实现技术

解密tar文件解压的Java实现技术 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 引言 在日常的软件开发和系统管理中&#xff0c;经常会遇到需要解压缩文件的…

代码随想三刷动态规划篇5

代码随想三刷动态规划篇5 377. 组合总和 Ⅳ题目代码 57. 爬楼梯&#xff08;第八期模拟笔试&#xff09;题目代码 322. 零钱兑换题目代码 279. 完全平方数题目代码 377. 组合总和 Ⅳ 题目 链接 代码 class Solution {public int combinationSum4(int[] nums, int target) {…

SM2的签名值byte数组与ASN.1互转

ASN.1抽象语言标记(Abstract Syntax Notation One) ASN.1是一种 ISO/ITU-T 标准,描述了一种对数据进行表示、编码、传输和解码的数据格式,它提供了一整套正规的格式用于描述对象的结构。 一、该结构的应用场景 例如在做待签名的数字信封时,数字信封使用ASN.1封装,这个时…

MySQL-行级锁(行锁、间隙锁、临键锁)

文章目录 1、介绍2、查看意向锁及行锁的加锁情况3、行锁的演示3.1、普通的select语句&#xff0c;执行时&#xff0c;不会加锁3.2、select * from stu where id 1 lock in share mode;3.3、共享锁与共享锁之间兼容。3.4、共享锁与排他锁之间互斥。3.5、排它锁与排他锁之间互斥3…

论文调研_Awesome-Binary-Similarity

0. 概述 对 Awesome-Binary-Similarity 中列出的论文进行调研,重点总结这些论文的研究动机与未来研究方向。 1. 调研内容 论文名称发表时间发表期刊期刊等级研究单位BinaryAI: Binary Software Composition Analysis via Intelligent Binary Source Code Matching2024年ICSE…

每日一题---OJ题:分隔链表

片头 嗨&#xff01;小伙伴们&#xff0c;大家好&#xff01;今天我们一起来看看这道题----分隔链表 emmmm&#xff0c;这道题&#xff0c;看描述应该不算太难&#xff0c;我们一起来画一画图呗&#xff01; 题目读懂了&#xff0c;那么如何破解这道题呢&#xff1f; 思路&…

microApp vue3+vite+ts 子应用接入改造

公司做了一个平台,使用的是microApp,需要把现有的一些系统作为子系统改造一下接入这个平台,所以下面我说的都是子应用的改造,vue2的改造比较简单,主要的vue3+vite+ts的改造。 参考官网 1、设置跨应用支持 vite默认开启跨域支持,不需要额外配置。 2、注册卸载函数 // …

nand flash spec

nand flash简介 nand flash是一种非易失性存储器。它具有高存储密度、低成本和高耐用性的特点。 nand flash的特性是非易失性&#xff0c;即在电源关闭的情况下&#xff0c;数据仍然保留。 nand flash的存储单元由浮动栅极晶体管组成&#xff0c;每个存储单元可以存储一位或多…

短视频世界对我温柔以待:成都柏煜文化传媒有限公司

短视频世界对我温柔以待 在繁忙的都市生活中&#xff0c;每个人都在为生活奔波&#xff0c;为梦想努力。而在这个快节奏的时代里&#xff0c;短视频如同一股清流&#xff0c;以其独特的魅力&#xff0c;为我带来了片刻的宁静与温柔。它像是一个无声的朋友&#xff0c;在我疲惫…

(必看图文)Hadoop集群安装及MapReduce应用(手把手详解版)

前言 随着大数据时代的到来&#xff0c;处理和分析海量数据已成为企业和科研机构不可或缺的能力。Hadoop&#xff0c;作为开源的分布式计算平台&#xff0c;因其强大的数据处理能力和良好的可扩展性&#xff0c;成为大数据处理领域的佼佼者。本图文教程旨在帮助读者理解Hadoop集…

Mysql面试合集

概念 是一个开源的关系型数据库。 数据库事务及其特性 事务&#xff1a;是一系列的数据库操作&#xff0c;是数据库应用的基本逻辑单位。 事务特性&#xff1a; &#xff08;1&#xff09;原子性&#xff1a;即不可分割性&#xff0c;事务要么全部被执行&#xff0c;要么就…

代码随想录1数组

1 二分查找 Leetcode704 1 [l,r]区间 l 0, r nums.length-1; while(l<r) 因为lr有意义 2 [l,r)区间 l 0, r nums.length; while(l<r) Leetcode35 class Solution {public int searchInsert(int[] nums, int target) {int l0,rnums.length;while(l<r){int m l(…

使用设计模式来增强你的 SpringBoot 开发

SpringBoot 是一个出色的框架&#xff0c;可以快速构建强大而高效的应用程序。但你是否知道设计模式可以将 SpringBoot 开发提升到一个新的水平&#xff1f; • 设计模式的重要性&#xff1a;了解设计模式如何促进代码的可重用性、可维护性和整体应用程序健康。 • SpringBoot…

在Spring Data JPA中使用@Query注解

目录 前言示例简单示例只查询部分字段&#xff0c;映射到一个实体类中只查询部分字段时&#xff0c;也可以使用List<Object[]>接收返回值再复杂一些 前言 在以往写过几篇spring data jpa相关的文章&#xff0c;分别是 Spring Data JPA 使用JpaSpecificationExecutor实现…

python 笔试面试八股(自用版~)

1 解释型和编译型语言的区别 解释是翻译一句执行一句&#xff0c;更灵活&#xff0c;eg&#xff1a;python; 解释成机器能理解的指令&#xff0c;而不是二进制码 编译是整个源程序编译成机器可以直接执行的二进制可运行的程序&#xff0c;再运行这个程序 比如c 2 简述下 Pyth…

运维锅总详解RocketMQ

本文尝试从Apache RocketMQ的简介、主要组件及其作用、3种部署模式、Controller集群模式工作流程、最佳实践等方面对其进行详细分析。希望对您有所帮助&#xff01; 一、Apache RocketMQ 简介 Apache RocketMQ 是一个开源的分布式消息中间件&#xff0c;由阿里巴巴集团开发并…

祝贺《华为战略管理法:DSTE实战体系》被《中国企业家》杂志评为企业家枕边书50本之一(宏观战略类书籍)

祝贺《华为战略管理法&#xff1a;DSTE实战体系》被《中国企业家》杂志评为企业家枕边书50本之一 2024年4月23日&#xff08;周二&#xff09;下午13:00&#xff0c;《中国企业家》杂志如期举办“每天都是读书日”线下活动。 《中国企业家》杂志携手商界大咖共同推选50本枕边书…

Vue.js中的计算属性

Vue.js中的计算属性&#xff08;computed properties&#xff09;是用于声明响应式依赖的属性。它们会根据它们的依赖进行缓存&#xff0c;并且只有在相关依赖发生改变时才会重新求值。这使得它们非常适合用来处理复杂逻辑和数据处理。 基本用法 在Vue实例中&#xff0c;可以…

镭速实现AD域集成助力企业文件安全传输管控

在当今这个信息量爆炸扩张的年代&#xff0c;企业数据宛如一座蕴藏无限价值的宝库&#xff0c;它不仅是企业核心竞争力的载体&#xff0c;也成为了各种潜在风险的聚焦点。随着数字化转型步伐的加快&#xff0c;安全文件传输的管理控制显得尤为重要&#xff0c;它构成了保护企业…

各类排序方法 归并排序 扩展练习 逆序对数量

七月挑战一个月重刷完Y总算法基础题&#xff0c;并且每道题写详细题解 进度:(3/106) 归并排序的思想也是分而治之 归并优点&#xff1a;速度稳定,排序也稳定 排序也稳定&#xff08;数组中有两个一样的值&#xff0c;排序之后他们的前后顺序不发生变化&#xff0c;我们就说…