解析Url地址(绝对路径解析)

/** * 绝对路径Url解析 * @param {String} url 要解析的url,Url必须带协议http或者https * @returns */ export const parseURL = (url) => { let a = document.createElement('a') a.href = url return { source: url, protocol: a.protocol.replace(':', ''), host: a.host, hostname: a.hostname, port: a.port, query: a.search, params: (function() { let params = {} let hashArr = a.search.replace(/^\?/, '').split('&') for (let i = 0; i < hashArr.length; i++) { let item = hashArr[i].split('=') params[item[0]] = item[1] || '' } return params })(), file: (a.

APP连接ESP8266——采用AT指令

1.主要实验设备及器材 1.1一块ESP8266(如图1) 图 1 ESP8266模块 1.2 一个USB转TTL模块(如图2) 图 2 USB转TTL模块 2.测试ESP8266模块 2.1连接设备 WIFI模块与USB转TTL模块进行连接,连接实物图如图3所示,硬件连线框图如图4所示。 图 3 连接实物图 图 4 硬件连接框图 2.2打开串口调试助手 本次实验使用的软件是XCOM V2.3,默认波特率为115200,停止位为1,数据位为8,校验位为None(如图5)。 图 5 XCOM V2.3 2.3输入测试指令AT 测试AT启动,返回OK (如图6) 图 6 AT测试 2.4复位指令AT+RST 输入AT+RST对WiFi模块进行复位操作,若返回ready代表复位成功(如图7)。 图 7 AT+RST 2.5 设置连接模式 输入AT+CWMODE=mode设置wifi应用模式,返回OK; mode: 1.Station模式 2.AP模式 3.AP+Station兼容模式 备注,station:客户端,AP:服务器; 本次测试采用Station模式,输入AT+CWMODE=1指令(如图8)。 图 8 AT+CWMODE=1 2.6连接wifi 这里输入AT+CWJAP=“BUNFLY loT studio”,“207207207”,返回OK(如图9)。 指令原型为:AT+CWJAP=<ssid>,<pwd> ,ssid就是wifi的名字,pwd就是wifi的密码。 图 9 AT+CWJAP 完成以上操作,就可以对ESP8266进行AT指令操作。 3.ESP8266操作指令 3.1 ESP8266做服务器操作 该操作是将ESP8266当做服务器,同手机或其他设备进行连接,操作步骤如下. 3.1.1设置多连接模式 输入AT+CIPMUX=1,返回OK(如图10)。 图 10 AT+CIPMUX 3.1.2建立服务器

2009~2021全国大学生数学竞赛初赛 重积分、曲线积分、曲面积分题目

以下截图来源于全国大学生数学竞赛官网和微信公众号考研数学竞赛 以下截图来源于全国大学生数学竞赛官网和微信公众号考研数学竞赛 以下截图来源于全国大学生数学竞赛官网和微信公众号考研数学竞赛 2021补赛 思路:积分区域为圆,换成极坐标 思路:积分恒等于0,被积函数就恒等于0. 被积函数是dydz+dzdx+dxdy,就化成dv 2021 思路:积分区域对称,积分式可能为0;积分区域为圆,积分式子凑x^2+y^2,并换成极坐标 2020 思路:把dx和dy分开,式子也分开;设原函数 思路:第一步是观察曲线的妙处。z无变化,则dz为0。第二步可以直接积分(但中间需要技巧);也可以把dx化成ds,这样被积函数f(x,y)为奇函数,即f(-x,-y)=f(x,y) 2019 思路:这个区域长啥样我不知道,就设它是一个罩子罩在xOy面上。换球坐标。(是什么坐标比较好我不知道,无非是柱坐标球坐标两种变换。高斯公式要把被积函数拆成三个再对相应变量积分,也不会用) 思路:先给他视为球坐标系,看出被积函数之后,换成直角坐标系并且是曲面积分,然后换元使得式子里只有一个变量 2018 思路:“路径无关”等价于“dx的对y求导=dy的对x求导”(定理不详) 思路:球就用球坐标系;大球减小球;球冠质量较难求↓ 2017 思路:x^2+y^2换坐标 思路:把x,y,z都换成只含有t的表达式 或者:斯托克斯公式 2016 思路:这个空间要算出来才能换对元 2015 看不懂答案 2014 思路:dydz,dzdx,dxdy都化成dv 2013 思路:积分最小值的几何意义 没做 2012 和路径无关,前面讲过 答案没看懂 2011 2010 2009

echarts 实现Y轴四等分

echarts 实现Y轴四等分* 需要实现的效果: 把整个Y轴实现四等分 同时保证Y轴最大的刻度 是距离最高的的数据 比如图上的100 是需要留出一些位置 比较低端 是第一个版本 后面实际使用是需要精进 此次的笔记 子是想记录一下 代码贴上 1.计算出 这七日的数据的最大值 最小值 2.计算出 最大值是几位数 3.计算出 最大值 对应的整百 整千… 4.计算出 最大值 的最高位是多少 向上整 (因为距离Y轴 是需要一点距离的 那么 就需要 往前加一个较大一点的数 所以会取最高位)需注意 1774 那么向上取整 后最高位是2;10 向上取整为1 5.做判断 向上取整后这个数(ceilData2 )是否比最大值大 如果没有 最高位+1 6 由于要做四等分 那么ceilData2 必须为可以被四整除的数 所以这边用了一个循坏 去向上加 取一个离他最近的一个可以被四整除的数 那么到这 其实对数据的处理就结束了 接下来 是对表格 进行属性的赋值 yAxis.put(“max”, ceilData2); // 把计算出来的这个 设置为Y轴刻度最大的值 yAxis.put(“splitNumber”, 4); // 设置为Y轴刻度 均分的个数 yAxis.put(“interval”,interval); // 设置 刻度的步长 interval =ceilData2/4;

MyBatis 获取参数指南

MyBatis 是一个轻量级,性能出色的半 ORM 框架,支持定制化 SQL,SQL 和 Java 代码分开,功能边界清晰。Java 代码专注于业务、SQL 语句专注于数据操作。 接下来我们学习一下 MyBatis 框架中,Java 代码如何传递参数,SQL 语句如何获取接口参数。 ${} 和 #{} 的区别 在 MyBatis 中,SQL 语句获取参数一般用 ${} 和 #{} 这两种方式,这两种方式的区别在于: ${} 使用字符串拼接的方式拼接 SQL 语句,容易发生 SQL 注入,一般不用这种方式,同时也需要频繁手动添加单引号。 #{} 使用占位符赋值的方式拼接 SQL 语句,有效防止 SQL 注入,当参数是字符串类型和日期类型时,能自动添加单引号。 SQL注入简介 上面提到 ${} 方式容易引发 SQL 注入,那么 SQL 注入是什么意思呢? 其实 SQL 注入是一种常见的攻击手段,攻击者在提交表单的时候额外添加一些额外的 SQL 语句,如果一些应用程序对用户输入的数据的合法性判断不严格或者没有过滤,可能会引发数据泄漏或者数据被篡改。 用一个简单的例子来说明: public interface UserMapper { List<User> findById(Integer id); int deleteById(Integer id); } <mapper namespace="com.example.UserMapper"> <select id="findById" resultType="User"> select * from t_user where id = ${id} </select> <delete id="

Windows安装Linux子系统

win10更新了安装linux子系统的步骤,使之更加方便,只需要在CMD运行命令: wsl --install,当然这会涉及很多默认的选项。 Windows 命令提示符中输入以下命令,找到可供安装的 Linux 发行版列表。 wsl --list --online 要安装 Ubuntu 默认版本以外的发行版,使用这个命令:wsl --install -d,如安装debian wsl --install -d Ubuntu-20.04 这个安装命令可以用于第一次安装,或者在你已经安装了 WSL 和默认的 Ubuntu 发行版之后再添加其他发行版。 使用 wsl --update 来手动更新你的 WSL Linux 内核,使用 wsl --update rollback 来回滚到之前的 WSL Linux 内核版本。 使用 wsl --status 查看关于 WSL 配置的一般信息,如默认发行版类型、默认发行版和内核版本。 附上官方地址:Windows Subsystem for Linux Documentation | Microsoft Learn

k8s管理工具kubectl详解(二)

目录 K8S模拟项目 一、项目的生命周期 二、创建kubectl run命令 使用run报错了 三、发布kubectl expose命令 1.service的作用 2.Service的类型 3.查看Pod网络状态详细信息和Service暴露端口 4.查看关联后端的节点 5.查看service的详细描述信息 6.访问内部IP查看 7.查看访问日志 四、更新kubectl set 1.获取修改模板 2.查看当前nginx的版本号 3.将nginx版本更新为1.15 4.监听pod状态 5.查看pod的IP变化 五、回滚kubectl rollout 1.查看历史版本 2.执行回滚到上一个版本 3.执行回滚到执行版本 查看历史版本 回到revision2,即1.15版本 4.检查回滚状态 六、删除kubectl delete 1.删除副本控制器 2.删除service K8S模拟项目 Kubectl是管理k8s集群的命令行工具,通过生成的json格式传递给apiserver进行创建、查看、管理的操作。 //帮助信息 [root@localhost bin]# kubectl --help kubectl controls the Kubernetes cluster manager. Find more information at: https://kubernetes.io/docs/reference/kubectl/overview/ Basic Commands (Beginner): create Create a resource from a file or from stdin. expose 使用 replication controller, service, deployment 或者 pod 并暴露它作为一个 新的 Kubernetes Service run 在集群中运行一个指定的镜像 set 为 objects 设置一个指定的特征 Basic Commands (Intermediate): explain 查看资源的文档 get 显示一个或更多 resources edit 在服务器上编辑一个资源 delete Delete resources by filenames, stdin, resources and names, or by resources and label selector Deploy Commands: rollout Manage the rollout of a resource scale 为 Deployment, ReplicaSet, Replication Controller 或者 Job 设置一个新的副本数量 autoscale 自动调整一个 Deployment, ReplicaSet, 或者 ReplicationController 的副本数量 Cluster Management Commands: certificate 修改 certificate 资源.

常见keil消除警告和报错

Warning: #1-D: last line of file ends without a newline 文件最后一行不是新行 解决:保证文件最后一行什么符号也没有 : missing return statement at end of non-void function “xxxx” 没有返回值 解决: 检查并在所指出的函数中添加返回值语句,通常是编写程序时候设定了返回类型但是没有设定返回值,需要注意的是这个问题有时候会导致建立文件时产生错误。 warning: #174-D: expression has no effect data 解决: 函数没有加上(),或者是data变量不能不能单独赋值,如要赋值就应该是 data = data warning: #111-D: statement is unreachable 解决: "statement is unreachable"这句一般是说编译器认为程序执行不到这里。 因为本人运行程序的时候,再向前有一个While(1)循环, 理论上说除非你里面有设置break,否则会一直循环下去。 编译器认为在while循环这里就会一直循环,永远跳不出来, 那么下面这一句指令也就永远无法执行到了。 这种警告一般不会影响程序的运行。 如果你觉得它很讨厌,可以修改你的语句,让编译器认为这个指令可以达到,比如加上一个If语句 error ..\ModulDrivers\driver_iic.c(112): error: #65: expected a ";" 解决:这种错误一般是符号错误,多一个符号或者少一个符号

MyBatis-Plus只查询固定条数记录

在使用MyBatis-Plus时有时候会碰到根据时间排序,只取最早或最晚一条记录的需求,这种需求其实只需在查询构造器最后加一个last()方法即可,方法里传入参数last(“limit 1”)。 这种方法可以实现上述需求,但是不得不写一个"limit 1",以后改需求查两个的话又需要改为"limit 2",而且这里写一个常量字符串感觉也怪怪的,基于此可以用两种方法代替。 方法一 使用常量代替,例如在代码中可以这样写 last(Constant.LIMIT_ONE) 方法二 自己建一个mybatis-plus查询构造器,继承LambdaQueryWrapper,实现代码如下: @Component public class StrengthenLambdaQueryWrapper<T> extends LambdaQueryWrapper<T> { /** * 只取一条记录 * @param lambdaQueryWrapper * @return LambdaQueryWrapper */ public LambdaQueryWrapper<T> limitOne(LambdaQueryWrapper<T> lambdaQueryWrapper){ return lambdaQueryWrapper.last(Constant.LIMIT_ONE); } } 在业务方法中使用 public void testLimitOne(String userName){ StrengthenLambdaQueryWrapper<SysBizUser> sysBizUserStrengthenLambdaQueryWrapper = new StrengthenLambdaQueryWrapper<>(); LambdaQueryWrapper<SysBizUser> lambdaQueryWrapper = sysBizUserStrengthenLambdaQueryWrapper .limitOne(sysBizUserStrengthenLambdaQueryWrapper) .eq(SysBizUser::getUserName, userName); SysBizUser sysBizUser = sysBizUserMapper.selectOne(lambdaQueryWrapper); }

ViewModel 源码设计思路分析

前言 转眼一年又过去大半了,在2022年,初定了大多计划,搬家,换公司,很多事情都一托再拖。 这里分享一篇我在公司内部做的分享文章吧,删除了部分对公司内部代码的探讨。 公司中的项目运用到了大量的组件封装。有的是对第三方组件进行二次封装,有的是从零开始设计,如何设计一个可扩展性高,容易使用的组件呢?可以参考参考google开发者是如何设计组件的。 我们以ViewModel组件为例,试图从源码中,窥探作者是如何设计ViewModel组件的。如果是我们,我们会怎么设计ViewModel组件呢? 需求分析 首先,需要明确我们的组件需要什么功能: 1、这个组件专门处理业务的数据逻辑 2、给Activity或者Fragment用的,希望同一个Activity下的fragment可以利用这个VeiwModel交互传递数据。 3、配置发生变化,导致View销毁重建的时候,保存这个ViewModel。数据不丢失。 4、ViewModel虽然不能持有Acitvity、fragment这种上下文,但是允许给一个Application。 5、进程意外杀死重建的时候,需要保存数据 头脑风暴(核心) 整理了需求之后,作者开始了头脑风暴,一起看看作者是如何思考的。 1、用于逻辑处理的类ViewModel。先搞个抽象父类,模板模式封装公共代码,逻辑交给子类。到时候用户继承我们的抽象类就好了。 2、保存这个功能要如何处理?我们希望在Activity或者Fragment中使用。并且到时候,一个Activity,可能有多个ViewModel。作者创建了一个新的类:ViewModelStore。专门用来保存一个界面的多个ViewModel。而VeiwModel负责处理业务逻辑,ViewModel的存储交给ViewModelStore.符合单一职责原则。 3、有了ViewModelStore保存我们的ViewModel了,那配置发生变化的时候,只需要存VeiwModelStore就好了。不是配置发生变化导致的界面销毁,就需要清空ViewModelStore中的ViewModel了。什么时候该保存,什么时候该清空,那就不是ViewModelStore这个类需要关心的事情了。因为ViewModelStore的责任只是用来保存ViewModel的。谁该关心呢?作者认为,谁持有,谁关心~作者创建了一个接口类ViewModelStoreOwner用来声明职责,给个方法,返回一个ViewModelStore,实现了这个接口,就会持有ViewModelStore了。那就由他去操心保存和清空ViewModelStore的功能吧。 可见,职责声明的场景,用接口来表示。 3.1、作者让ComponentActivity实现了接口,并在构造函数中监听生命周期回调。当生命周期走向onDestroy的时候,判断当前是不是因为配置变化导致的销毁Activity,不是的话,就清空ViewModelStore中的数据。 3.2、那具体怎么保存数据呢?当因为配置发生变化的时候,Acitvity的生命周期被安排走向尽头。在ActivityThread中,调用activity的retainNonConfigurationInstances。该方法返回一个NonConfigurationInstances对象(可以认为包装类),把对象用变量lastNonConfigutationInstances保存在ActivityCliendRecord中。在该方法中 NonConfigurationInstances retainNonConfigurationInstances() { //该方法是空实现,用于保存子类的对象 Object activity = onRetainNonConfigurationInstance(); //...忽略代码 NonConfigurationInstances nci = new NonConfigurationInstances(); nci.activity = activity; //...忽略代码 return nci; } 所以ComponentActivity实现onRetainNonConfigurationInstance并返回ViewModelStore对象即可。 不过,作者明白,只有一些刁民,喜欢瞎搞事情。 为了防止这些刁民,作者在ComponentAcitvity中把这个onRetainNonConfigurationInstance方法写了final,防止他们重写了方法,覆盖了我们的代码。这个方法就用来给我们保存数据的. 但是做人做事都不能赶尽杀绝啊,毕竟大部分的用户都是遵纪守法的好公民,这个方法只保存我们的ViewModelStore,也太局限了。于是,作者就创建了一个空函数,用户实现这个方法,返回他们想要正常保存的数据,到时候,连带帮用户的数据一起存起来不就好了。所以到时候补一个用户实现这个方法,配置发生变化的时候,顺带帮他们一起保存了。既然要存的有2种数据,一种是用户的,另一种是ViewModelStore。那就需要创建一个包装类了,把这俩包装起来。于是ComponentActivity中,就需要创建一个静态内部类了。 这种实现父类给的方法,但是不赶紧杀绝的做法很赞,在我们的项目中,也发现了类似的写法。此处省略一万字。。。。。 当Activity重新创建的时候,再来个方法返回我们之前保存的数据就好了~ 在Activtiy的attach函数中,会把之前的配置传回来。 这样子类在就可以通过这个对象获取之前我们存储的ViewModelStore了。 如此一来,保存数据和获取数据的方案都有了~ 到目前为止,我们已经有一个专门处理逻辑的类ViewModel,一个专门保存ViewModel的类ViewModelStore,以及利用接口ViewModelStoreOwner去声明相关职责了。保存清空的思路也都有了。 4、下面就需要考虑如何去创建了 用户是可以自由定义ViewModel的具体的类的,只需要继承我们的ViewModel就好了,并且ViewModel的构造函数中,可能有用户自己的参数。也可能没有参数或者是有一个Application的参数。这种情况下,作者想到了利用工厂模式,为每一个ViewModel提供一个工厂类,如果有构造函数有自定义的参数,那就自己搞个工厂类去创建,没有参数或这有一个Application参数的时候,由我们提供的工厂创建就好了。 工厂类接口很简单: 作者为没有参数的构造函数和参数只有一个Applicaiton的构造函数,创建一个默认的工厂类NewInstanceFactory和AndridViewModelFactory 使用反射直接获取具体的类对象。带有Application参数的工厂类也是一样的,只是多了一个参数而已。问题不大:AndroidViewModelFactory 5、创建的方法已经有了。下面就是思考如何获取拿到ViewModel实例对象了。为什么要这么考虑呢?利用工厂模式不是已经可以获取到实例对象了? 因为我们有存储机制,当配置变化的时候,不再是通过factory获取了,而是从ViewModelStore中获取。 先整理一下获取ViewModel需要的相关逻辑: (1)获取到对应的工厂实例对象 (2)从viewModelStore中获取ViewModel,或者利用工厂去创建一个ViewModel。 (3)创建之后,同时需要保存ViewModel。 这三部就是创建的流程需要涉及的相关逻辑 对于用户而言,他不应该关心如何保存我们的ViewMdel,什么时候从ViewModelStore中获取,什么时候创建,这些都应该是用户无感知的。这遵循迪米特原则:最少知道原则,用户在调用ViewModel组件的时候,不应该知道这个类的内部细节,以及内部调用逻辑。比如说外观者模式,或者中介者模式就符合迪米特原则。外观者,封装了组件内部实现细节,并提供统一的接口或者函数给外部。也降低了使用成本。这种最常见的就是Glide。简单的一行内码,就可以整合了内部千军万马的组件调用逻辑。 作者创建了一个新的类:ViewModelProvider。对外提供一个get函数,利用传入的具体的class,使用对应的工厂创建ViewModel。内部逻辑怎么调用,与用户无关。 这个ViewModelProvider做了什么事情呢? 1、根据不同的情况,拿到对应的工厂类。 2、从缓存中获取ViewModel,拿不到的时候,利用工厂类创建ViewModel。 3、把ViewModel保存到ViewModelStore中。

虚拟机连不上网络 -- 解决

前言: 在window系统上安装虚拟机,发现虚拟机启动后连接网络失败!!! 原因一: 检查本地虚拟机服务是否正常启动; Win+R -> services.msc -> 搜索VM,检查一下选项服务是否都开启; 原因二: 检查虚拟机的网络适配器; 选择NAT模式!!!

用新服务器从零开始部署 DolphinDB

本文主要介绍在新服务器部署 DolphinDB 时需要注意哪些系统配置,以及如何选择 DolphinDB 部署方式以符合业务需求。合适的系统配置可以提高 DolphinDB 系统的稳定性和可维护性,而合适的部署方式可以提高业务执行的效率。 1. 操作系统配置 本文以 CentOS 7.9.2009 为例,介绍 DolphinDB 相关的系统配置方法。 1.1 平台要求与推荐 1.1.1 支持的平台 平台 处理器架构 是否支持 Linux x86 是 arm 是 龙芯 是 Windows x86 是 Mac - 否 BSD - 否 在 Linux 系统使用 DolphinDB 要求内核版本为 Linux 2.6.19 或以上,推荐使用 CentOS 7 稳定版。 1.1.2 依赖软件 DolphinDB 依赖 gcc 4.8.5 或以上版本。以在 CentOS 7 稳定版上安装为例: # yum install -y gcc 1.1.3 推荐硬件配置 需要为 DolphinDB 元数据,redo log 以及数据实体配置不同的硬盘,以优化系统性能。

如何解决 Hydro OJ 502 Bad Gateway 问题 | Hydro爆炸502解决方案

目录 遇到问题尝试解决解决问题 遇到问题 有一天我心血来潮在自己的服务器上部署了一个 Hydro OJ (http://zqy.ac.cn:8888/),在捣鼓了一整天,基本成型的时候,他却在装了一个自动导入题目的插件之后玩废了。 尝试解决 什么?这么费力捣鼓出来的一个OJ,就这样享年一天?我自然是不甘心的。于是我尝试去解决它。删掉插件,没用。重启服务端程序,没用。重启服务器,还是没用。在网上也搜不到解决方案,因为没有遇到和我一样的问题的。 解决问题 既然没有现成的解决方案,于是我尝试去自己解决这个问题。经历了坎坎坷坷后,我终于能够访问OJ了,并且还恢复了部分数据。 我使用的解决方案如下: 出现问题之后依旧是可以在后台备份的,使用 hydrooj backup 对 HydroOJ 进行备份。重置HydroOJ,需要注意的是,重置之后所有的数据都将被删除,因此务必备份好之后再重置。 将此脚本下载到服务端执行,即可重置。还原数据。使用 hydrooj restore backup-xxx.zip 可以对数据进行还原,只能够还原一部分数据(讨论、题库、比赛、公告、用户等),系统设置无法还原,需要手动重新设置。重新启动HydroOJ,现在问题也许可以得到解决。

谷歌浏览器无法使用谷歌翻译解决办法

喜欢用谷歌浏览器的小伙伴最近可能有个烦恼,在看一些英文网站的时候,谷歌翻译出现了问题,无法翻译,这可恼心了,咋办呢? 那是因为不久前谷歌关闭了中国地区的翻译引擎服务,据说其原因是在中国大陆使用率太低,在9月28日退出了中国市场,如果你使用梯子或者其他方法也无法成功复活Google翻译,以至于Google翻译影响正常你浏览网页。点击下方链接,进行下载解压运行,点击.exe文件运行即可,在运行的过程中会弹出DOS命令窗口,这个过程中等待一两分钟即可,最后会提示你输入Y进行确认。就可以将最佳的ip添加到host文件中。如果浏览器翻译失败,重新运行该程序即可。 未翻译前 使用翻译后,又可以进行愉快的使用 Window系统链接 链接:https://pan.baidu.com/s/1dVhNaZ4sEhC75sPB2P3fxA 提取码:6cb4。 Linux系统链接 链接:https://pan.baidu.com/s/1MUPGA-mRLHWk2p6hNrPIxw?pwd=r895 提取码:r895 Mac系统链接 链接:https://pan.baidu.com/s/1Eilhf7GVZyMXzcx63bL-MA?pwd=gc3o 提取码:gc3o 可以通过百度网盘或者搜索公众号聊天框回复:谷歌翻译 最后,如果各位小伙伴有什么不懂的话,可以扫描下方二维码咨询哦!!! 感谢各位的支持!!!

软件测试项目实战案例分解,跟着我一步一步操作【人力资源管理系统】

目录 一、引言 二、项目概述 三、平台、角色和权限 四、人资管理员 五、最后 一、引言 1.编写目的 本文档将列举实现人力资源管理系统(以下简称HR系统)所需要的全部功能,并对每个功能给出简单的描述。 本文档的预期读者包括:最终用户,项目负责人,评审人员,产品人员,软件设计开发人员,测试人员。 2.背景 在全球一体化浪潮和新技术革命的不断推动下,人力资源在人类社会经济生活中处于越来越核心的地位;未来的经济竞争,不再是物质资源或物质资本,人力资源成为最根本的竞争优势。如何围绕企业宗旨、针对各类人员特点及企业的管理现状“设计出实用有效的人力资源管理系统,从而实现由人工管理向计算机管理的转型,使得人力资源管理工作变得更为客观有效,优化配置、提高办学效益”,成为企业人力资源管理系统设计面临的首要问题。 3.名词、缩略语 名词/缩略语 解 释 ID 唯一标识码 UI 软件的人机交互界面 二、项目概述 1.建设目标 本项目的目标是建立符合一般企业实际管理需求的人力资源管理系统,对企业的人力资源信息进行精确的维护,有效服务,从而减轻HR管理部门从事低层次信息处理和分析的负担,解放管理员的“双手大脑”,提高工作质量和效率。 2.技术要去 本项目软件系统平台将达到主流Web应用软件的水平: (1)功能方面:系统满足业务逻辑各功能需求的要求。 (2)易用性方面:通过使用主流的浏览器/服务器架构,保证用户使用本系统的易用性良好。 (3)兼容性方面:通过系统设计以及兼容性框架设计,满足对主流浏览器兼容的要求。 (4)安全性方面:系统对敏感信息(例如用户密码)进行相关加密; (5)UI界面方面:界面简洁明快,用户体验良好,提示友好,必要的变动操作有“确认”环节等。 三、平台、角色和权限 HR系统涉及Web端平台及手机APP(Android),Web端管理系统包含系统管理员、人资管理员、普通职员三个角色,APP包括人资管理员及普通职员角色。 1.web端 Web端分为系统管理员、人资管理员、普通职员三个角色;系统管理员主要对用户及用户角色进行管理,并维护一些通用的字典;人资管理员则主要维护组织机构、员工基本信息及员工劳动合同、薪酬、证书、培训进修、奖惩等信息;普通职员登录系统后,可修改个人基本信息,并查阅个人相关证书、培训进修、奖励等信息。 (1)系统管理员 (2)人资管理员 (3)普通人员 内置角色 一级模块 二级模块 功能项 系统管理员 系统用户管理 查询、角色配置、启用、禁用、重置密码 类别维护 岗位类别 创建类别、修改、上移、下移 政治面貌类别 创建类别、修改、上移、下移 学历层次类别 创建类别、修改、上移、下移 学制类别 创建类别、修改、上移、下移 合同终止原因类别 创建类别、修改、上移、下移 角色名称 一级模块 二级模块 三级模块 功能项 人资管理员 组织机构管理 添加部门、修改、删除、指定负责人、删除部门负责人、查看部门详情 岗位管理 岗位管理 添加岗位、修改、查询、下载岗位说明书 员工基本信息管理 员工信息管理 录入员工、编辑、离职、查看详情、修改工号、查询、导出

微信授权登录 | 全过程讲解[官方文档->代码梳理->注意点] uniapp+springboot(附Gitee源码)

目录 🚢微信登录官方文档流程梳理🚢 🛳️反推过程🛳️ ⛴️1.获取用户信息⛴️ 🛥️ 2.获取access_token 和 openid🛥️ 🚤 3.获取code🚤 🚁视频源码分享🚁 🚢微信登录官方文档流程梳理🚢 核心目标: 通过用户微信认证 获取用户信息 🛳️反推过程🛳️ ⛴️1.获取用户信息⛴️ 获取用户个人信息(UnionID 机制) 官网链接 GET https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID ​ 返回结果: 微信获取信息变更 { "openid": "OPENID", "nickname": "NICKNAME", "sex": 1, "province": "PROVINCE", "city": "CITY", "country": "COUNTRY", "headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0", "privilege": ["PRIVILEGE1", "PRIVILEGE2"], "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL" } 🛥️ 2.获取access_token 和 openid🛥️ 用户请求接口需要 参数access_token 和 openid 通过 code 获取 token 官网链接 GET https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code ​ 返回结果: { "access_token": "ACCESS_TOKEN", "expires_in": 7200, "

Linux计划任务at和cron命令的使用

文章目录 前言 什么是计划任务? 一次性调度执行at 简介 服务启动 语法 参数: 示例 提交at作业 提交步骤 时间选择格式 示例 查看任务 示例 查看任务执行日志 示例 删除任务 示例 循环调度执行crontab命令 简介 查看服务(是否启动) 创建计划 编辑器中的语法格式 示例 代码中涉及到的任务信息解读 查询计划 删除计划 总结 前言 前面学习了Linux中的软件管理,这篇文章将开始学习Linux中的计划任务,通过本篇文章将学习Linux中的一次性调度执行at命令和循环调度执行cron命令的介绍与使用,下面开始我们的Linux计划任务的学学习吧。 什么是计划任务? 划任务是系统的常见功能,利用任务计划功能,可以将任何脚本、程序或文档安排在某个最方便的时间运行。任务计划在每次系统启动的时候启动并在后台运行。当我们需要在服务器上定时执行一些重复性的事件时使用的,可以通过计划任务程序来运行准备好的脚本、批处理文件夹、程序或命令,在某个特定的时间运行,目前最主要的用途是定期备份数据。。 一次性调度执行at 简介 有些场景我们只是需要执行一次,执行完就结束任务我们使用at命令来操作,任务的执行日志以邮件的形式反馈给电脑。 服务启动 默认情况下所有的linux系统都提供此项功能。在redhat中atd是默认随系统启动而启动。 [root@localhost ~]# ps -ef |grep atd root 1284 1 0 06:25 ? 00:00:00 /usr/sbin/atd -f root 12970 12205 0 10:51 pts/0 00:00:00 grep --color=auto atd 您在 /var/spool/mail/root 中有新邮件 [root@localhost ~]# systemctl status atd.

制作 设置 谷歌Google 火狐Firefox 跨域 允许跨域设置的方法

谷歌-第一步 在D盘创建一个文件夹 ChromeDevSession (注:文件名字可以自定义) 谷歌-第二步 找到谷歌exe文件,创建谷歌快捷方式。如图: 谷歌-第三步 谷歌快捷方式修改一下名称,方便区分。如图: 谷歌-第四步 设置谷歌快捷方式属性。选中谷歌快捷方式,鼠标右键-选中属性。如图: 谷歌-第五步 在 ‘目标(T):’ 后尾添加 --args --disable-web-security --user-data-dir=“D:/ChromeDevSession” 点击应用 点击确定 (注:不要去修改谷歌的路径;这里的"D:/ChromeDevSession" 是我们第一步创建的文件夹,名称要一致) 例如:C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome.exe --args --disable-web-security --user-data-dir=“D:/ChromeDevSession” 谷歌-第六步 打开跨域谷歌浏览器,提示 --disable-web-security 说明你设置成功了。 火狐-第一步 在地址栏输入 about:config 会出现如下图的页面 点击 ‘我了解此风险!’ 点击 ‘我了解此风险!’后呀,就会出现下面的页面 在搜索框里输入 ‘security.fileuri.strict_origin_policy’ 就把 ‘security.fileuri.strict_origin_policy’ true 改成 false 重启浏览器,可以跨域访问 有的时候 不好使没有生效,需要在安装一个插件 (CORS Everywhere) 插件传送门 安装成功后 会在地址栏区域出现一个cores 的小图标 绿色表示打开 红色表示关闭

Cookie的使用(基于js-cookie插件)

简介: Cookie 是一段不超过4KB的小型文本数据,由一个名称(Name)、一个值(Value)和其它几个用于控制 Cookie 有效期、安全性、使用范围的可选属性组成,储存在用户本地终端上。 js-cookie 是一个简单的,轻量级的 处理cookies的 js API。 一、安装和引入 1、安装 npm install js-cookie --save 2、引入 import Cookies from 'js-cookie' 二、使用 1、存储 cookie 值 Cookies.set('name', 'value', { expires: 7, path: '/', domain: '', secure: false }) name: cookie 的变量名。 value: cookie 变量的值。 expires: 设置 cookie 变量保存的时间,单位是天。 path: cookie 的有效范围,默认为“/”。path 是在参数 domain 基础上的有效范围。 示例:path 设置为 ”/”,在整个 domain 都有效;path 设置为 ”/test”,则只在 domain 下的 /test 目录及子目录才有效。 domain: cookie有效的域名。 示例:domain 设置为 googlephp.cn,那么在 googlephp.

大整数运算(高精度运算)C/C++

前言 这种类型,在做题过程中多为观察所给数据可能造成的大小来选择是否使用。 属于模板类型,学习者理解其格式并记住大致框架即可熟练应用。 一、什么是大整数(高精度) 想知道什么是大整数,不如换一个解释的方法:什么时候需要用到大整数? 众所周知,int类型的范围是-2147483648~2147483647。long long类型的范围是 -2^63 ~ (2^63)-1。那当题目需要用到或需要输出比long long类型的范围还要大的数字时,我们是不是就不能用常规办法去接收这些数字了。这个时候使用大整数就可以解决上述问题。即解决接收超出long long范围数字的问题。 二、大整数的存储 原理很简单,使用数组。 例如定义Int类型数组d[1000],那么这个数组中的每一位就代表存放整数的每一位。比如将数字123456存储,则有d[0] = 6,d[1] = 5,d[2] = 4,d[3] = 3,d[4] = 2,d[5] = 1。整数的高位存在数组的高位,整数的低位存储在数组的低位。(为了契合加减乘除的思维) 这时候会产生一个需要注意的问题:把整数通过%s或者itoa的方式变成字符串的时候,一开始是逆位存储的,即d[0] = 1...,因此读入的之后需要在另存为至d[i]数组的时候反转一下。 为了方便随时获取大整数的长度,一般定义len来记录长度,并与d数组组合成结构体。 struct bign{ int d[1000]; int len; }; bign是big number的缩写。 一般输入大整数时,都是先用字符串读入,然后再把字符串另存为至bign结构体。由于使用char数组进行读入时,整数高位会变成数组地位,整数的地位会变成数组的高位。因此为了让整数在bign中是顺位存储,需要让字符串倒着赋值给d[]数组。 bign change(char str[])//将整数转换为bign { bign a; a.len = strlen(str);//bign的长度就是字符串的长度 for(int i = 0; i < a.len; i++) { a.d[i] = str[a.len-i-1] - '0'; } return a; } 如果要比较两个bign变量的大小,也很简单:先判断len大小,若不相等,长的为大,若相等,则从高位至低位逐个比较,直到某一位不等,则可以判断两者大小。

Tensorflow.keras搭建模型报错:

卷积和池化不能随便用?要计算维度 维度要对应 用tensorflow.keras的sequential方法随便搭建了一个网络 加入这一行的时候报错 ValueError: Negative dimension size caused by subtracting 3 from 1 for '{{node conv2d_1/Conv2D}} = Conv2D[T=DT_FLOAT, data_format="NHWC", dilations=[1, 1, 1, 1], explicit_paddings=[], padding="VALID", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](conv2d/Identity, conv2d_1/Conv2D/ReadVariableOp)' with input shapes: [?,1,222,64], [3,3,64,128]. 似乎是维度无法对应 也有人说是keras后端对图片的通道顺序和tensorflow不一样 改过之后还是不对 个人认为是池化尺寸(2,2)这里写的不太对 要按照标准写法 参考: 或者是Conv2D的kernel_size写的不对? 经验证 应该是Conv2D的kernel_size 改成kernel_size=3 就好了 用函数时一定要查一下标准的用法才行!!!不要自己瞎写 很奇怪的一点是 改过kernel_size跑通一次之后再改回原来的代码 就不报错了?不知道这里发生了什么 是涉及到后端的一些操作了吗? 查了一下官方文档 Conv2D layer 官方文档 来自 <Conv2D layer> kernel_size: 一个整数或 2 个整数的元组/列表,指定 2D 卷积窗口的高度和宽度。可以是单个整数,为所有空间维度指定相同的值。 看来3或者(3,3)都可以~ 还是不太明白中间发生了什么

Java控制台实现循环计算加减乘除

Java控制台实现循环计算加减乘除 前言 对于刚刚学习Java的同学,在Java中控制台实现循环计算简单的加减乘除还是比较简单的,主要在于循环设计的思路,实现方法有很多,别人的不一定是最简单最好的,小伙伴一定要注重自己的思维逻辑,下面说一个案例思路,欢迎小伙伴留言一起学习。 主要思路 控制台实现循环计算加减乘除,要从控制台获取输入数据(需要使用Scanner对象创建一个扫描器对象,用于接受键盘数据),如何循环获取键盘接受数据(通过while循环保证程序不满足设定条件是,可以一直循键盘输入值),或者如何循环的选择计算的加减乘除(选择加减乘除的功能,if,for,while,switch改用那个?相信小伙伴们已经有答案了switch循环解决),总体思路有了,程序就解决一半了。 Scanner对象 之前我们学的基本语法中我们并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,我们可以获取用户的输入。java.util.Scanner是Java5的新特征,我们可以通过Scanner类来获取用户的输入。 基本语法: scanner s = new Scanner(System.in); 通过Scanner类的next()与nextLine()方法获取输入的字符串,在读取前我们一般需要使用hasNext()与hasNextLine()判断是否还有输入的数据。 注意事项:next()与nextLine()方法获取输入的字符串有所不同,这里就不说了,小伙伴们可以实战一下就明白。 while循环和switch循环 while循环基本语法: //循环的条件,默认是true执行 while(循环的条件){ ... 循环体 ... } switch循环基本语法: switch(expression){ case value ://语句 break;//可选 case value : //语句 break;//可选 //你可以有任意数量的case语句default : //可选 //语句 } 实战 完整代码: package com.sqgxy.method;//这是包名可以省略删除 import java.util.Scanner; public class Demo06 { public static void main(String[] args) { String str="y"; while (str.equals("y")) { Scanner scanner=new Scanner(System.in); System.out.println("请输入你要选择的功能:加法(0)、减法(1)、乘法(2)、除法(3)"); if (scanner.hasNextInt()) { int x = scanner.

Kubectl

目录 一、资源管理 1.kubernetes集群管理 2.kubectl 3.kubectl 的命令 (1)查看版本信息 (2)查看资源对象简写信息 (3)查看集群信息 (4)配置kubectl自动补全 (5)node节点查看日志 (6)K8s核心组件日志 二、基本信息查看 1.查看master节点状态 2.查看命令空间 3.查看default命名空间的所有资源 4.创建命名空间app 5.删除命名空间app 6.描述某个资源的详细信息 7.查看命名空间kube-public 中的pod 信息 8.扩缩容 9.删除副本控制器 10.定义自动扩容、缩容规则 三、项目的生命周期 1.创建kubectl create命令 2.发布kubectl expose命令 3.查看pod网络状态详细信息和service暴露的端口 4.查看关联后端的节点 5.查看service的描述信息 6.在节点上操作,查看负载均衡端口 (1)node01 (2)node02 (3)查看访问日志 四、更新kubectl set 1.更改现有应用资源一些信息 2.获取修改模板 3.查看当前nginx 的版本号 4.将nginx版本更新为1.21版本 5.更新好后的Pod的ip会改变 6.再看nqinx的版本号 五、回滚kubectl rollout 1.对资源进行回滚管理 2.查看历史版本 3.执行回滚到上一个版本 4.执行回滚到指定版本 5.检查回滚状态 六、删除kubectl delete 1.删除副本控制器 2.删除service 3.金丝雀发布(Canary Release) (1)更新deployment的版本,并配置暂停deployment (2)监控更新的过程 (3)确保更新的pod没问题了, 继续更新 (4)查看最后的更新情况 七、声明式管理方法 1.查看资源配置清单 2.解释资源配置清单 3.修改资源配置清单并应用 4.删除资源配置清单

Mybatis-Plus 代码生成器,自定义模板Demo,快速搭建!!

Mybatis-Plus 代码生成器,自定义模板Demo,快速搭建!! 前言 详细信息以及具体配置方法解析–》官方文档:https://baomidou.com/ 目录: Mybatis-Plus 代码生成器,自定义模板Demo,快速搭建!!前言1.所需maven :2.代码生成配置3.自定义模板3.1 公共基础类3.2 自定义模板(vm)3.21 controller模板3.22 service模板(vm)3.23 serviceImpl模板(vm)3.24公共查询基础模板(vm): 1.所需maven : velocity 和 freemarker 选择导入,velocity 对应vm的模板,freemarker 对应flt模板,自定义模板,需要将导入进的配置下的模板复制到自己项目的resource下 01:Mybatis-Plus 下的模板 02:自己项目 resource 新建 templates 03:maven: <dependencies> <!-- mybatis plus 代码生成器 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> 2.代码生成配置 package cn.hb.ycmm.basic; import com.baomidou.mybatisplus.annotation.FieldFill; import com.

谷歌浏览器无法翻译此网页

原因:谷歌关闭了中国大陆的谷歌翻译服务 方案一:通过 hosts 把谷歌翻译的 API 指向国内可用服务器地址 一、按 Win + R 键打开运行 ➡ 输入 cmd 回车 ➡ 输入 ping google.cn 回车。如下图,拿到 ping 成功的 ip,复制 ip 二、找到 C:\Windows\System32\drivers\etc ,目录下的 hosts 文件,用记事本打开,在最下面加入以下文本(已经成功的 ip ),注:ip 和 translate.googleapis.com 之间有一个空格 172.217.215.90 translate.googleapis.com 172.253.115.90 translate.googleapis.com 142.250.189.206 translate.googleapis.com 142.250.126.90 translate.googleapis.com 142.250.10.90 translate.googleapis.com 142.250.0.90 translate.googleapis.com 120.253.255.34 translate.googleapis.com 可能保存hosts文件的时候会提示没有权限,那就更改权限,右键hosts文件 ➡ 属性 ➡ 安全 三、按 Win + R 键打开运行 ➡ 输入 cmd 回车 ➡ 输入 ipconfig /flushdns 回车,刷新DNS 补充:目前上述 IP 是能用的,如果失效,可以使用步骤一,ping 成功之后复制 ip,修改hosts

Graphviz 可视化图形软件(python)

目录 1.简介 2.Graphviz 工具安装 3.检查是否安装成功 4. Graphviz 库安装 5.验证安装的graphviz是否可用 6.绘制红酒数据集得到的决策树: 7.问题:pycharm正常画决策树,但jupyter显示:ModuleNotFoundError: No module named 'graphviz' 1.简介 Graphviz 是一款由 AT&T Research 和 Lucent Bell 实验室开源的可视化图形工具,可以很方便的用来绘制结构化的图形网络,支持多种格式输出。Graphviz 输入是一个用 dot 语言编写的绘图脚本,通过对输入脚本的解析,分析出其中的点、边及子图,然后根据属性进行绘制。Graphviz layout 以简单的文本语言描述图形,并以实用的格式制作图表,如用于网页的 images 和 SVG ;用于放入在其它文件中或显示在交互式图形浏览器中的 PDF 和 Postscript 。 对于 Python 而言,也有一个 Graphviz 库与该工具对应,使用 Graphviz 库可以使用 Python 的语法生成 dot 语言绘图脚本,并调用 Graphviz 工具生成图片并保存。但是由于 Graphviz 库并不是独立工作的库,它需要连接 Graphviz 工具来联合工作,也就是说当你需要使用 Graphviz 库时必须安装 Graphviz 工具并设置好 Python 调用 Graphviz 工具的接口。 不会出错的安装 如果我们需要在Python中直接将.dot文件转成pdf显示出来,就需要导入库,安装库也很简单,只需要执行pip install graphviz 即可。 2.Graphviz 工具安装 官网地址

3-MySQL常用数据类型及表管理

数据类型 1.整数类型:只能包含数字的类型,不包含小数点 TINYINT:占用1字节存储空间(1B) SMALLINT:占用2字节存储空间(2B) MEDIUMINT:占用3字节存储空间(3B) INT:可以动态设置数据占用的存储空间 BIGINT:占用8字节存储空间(8B) 2.浮点类型:带小数点的数 FLOAT :单精度,4字节 DOUBLE:双精度类型,8字节 DECIMAL:定点数类型,可以固定小数点后数据的位数(精度) 3.字符串类型 char:固定长度的字符串类型,比如邮政编码char(6),身份证号char(18),长度不能超过8000字节 VARCHAR:不固定长度的字符串类型,设置最大数据范围,比如varchar(100),长度不能超过8000字节 BINARY:固定长度的二进制,长度不能超过8000字节 VARBINARY:不固定长度的字符串类型,长度不能超过8000字节 BLOB:大文件操作 text:大文本数据操作 enum:限定范围(‘男’,‘女’) set:简单理解为数组集合 4.日期与时间类型 year:年 date:日期(包含年月日) time:时间(包含时分秒) datetime:日期时间(包含年月日时分秒) TIMESTAMP:时间戳,精度比datetime要高 5.二进制类型 bit:1位 BINARY VARBINARY BLOB TINYBLOB MEDIUMBLOB LONGBLOB -- 创建数据表create table 语句 /* 创建学生表(学生表) 学号 整形,不允许为空(not null) ,主键(primary key) 姓名 字符串,最大长度20字符 年龄 整形 ,必须大于0 班级 字符串 性别 字符串 */ -- 删除表格 drop table 学生表; /* 字段名称必须唯一, 字段名称中间最好不要出现空格,如果字段名称包含空格,必须加上`符号 字段名称要有一定的意义 */ create table 学生表

电脑录屏怎么录?3个方法,教你如何录制视频

电脑录屏是可以通过录制视频的方式进行记录下我们的操作过程,并且可以随时保存下来方便以后查阅使用。大家都知道,电脑录屏是非常实用的,能够帮助我们记录屏幕上的内容,而且还能保存下来,以便我们以后的电脑学习使用。 电脑录屏怎么录?本文将将会带来3个实用功能,教你如何录制视频。那么那么就一起来看一下吧! 电脑录屏怎么录方法1:电脑自带的步骤记录器 电脑怎么录制屏幕?借用Windows的电脑都自带一个工具:步骤记录器。这个系统步骤记录器一般用于需要简单的图文教程的制作,不需要任何操作就能够对电脑进行自动录下来。操作步骤如下: 首先,点击开始菜单,选择【运行】,也可以快捷键【Win+R】,在窗口中,输入”PSR”,调出问题步骤记录器。 接下来,点击开始录制,即可录制屏幕。在录制过程中有“开始记录”、“停止记录”、“添加注释”、“暂停记录”、“恢复记录”的按钮录制完成,点击结束录制就可以了。 电脑录屏怎么录方法2:使用电脑QQ的录屏功能 不想安装其他软件,怎么在电脑上录制屏幕视频 ?QQ录屏的方法很简单,只需要同时按下快捷键Ctrl+Alt+S就能启动录屏功能,这是非常重要的一个步骤,然后选择需要录制的区域开始录制,点击结束,录制的内容可以自己保存下来然后修改视频名称,以方便以后查阅使用了。 第1步:打开要发送录屏的聊天窗口; 第2步:点击下图所示截图图标,选择“屏幕录制”,也可直接快捷键ctrl+alt+S; 第3步:然后选择要录制的区域,设置需要录制的屏幕大小; 第4步:选择区域后点击右下角的“开始录制”;录制前可以设置扬声器和麦克风,录制完毕点击【结束录制】。 电脑录屏怎么录方法3:安装专业的电脑录屏软件 想在电脑上录屏,还可以借用专业电脑录屏软件。这个软件相对就成熟很多,也是录屏软件中的老牌选手,不但功能齐全,操作也很简单,可满足绝大多数小白用户的录屏需求。软件支持录制电脑屏幕上的操作过程,包括软件操作、网络教学、课件制作等,录屏的同时录制麦克风和电脑声音。实现视频、音频、画质、录制格式、录制模式等多种选项。操作步骤如下: 步骤1.打开安装好的数据蛙录屏软件,之后点击【视频录制】,在录制界面,声音来源选择全部(支持麦克风和扬声器,其中麦克风是指外部声音,而扬声器是指录制电脑内部声音),并根据您的需要打开摄像头。 如下图,视频支持全屏录制和自定义区域录制。 步骤2.在电脑录屏的过程中,根据自己的实际情况进行配置参数,保证视频质量,有利于视频的剪辑。这里可以对视频的清晰度、帧率、音量、声源、麦克风、网络延迟、帧率优先级等信息进行设置。完毕后,点击【开始录制】按钮,显示录制计时3秒,代表开始全屏录制视频。 备注:需要根据实际情况对音频降噪处理,音频质量更好些。 步骤3.屏幕录像结束后,点击【停止录制】按钮,软件自动弹出视频预览窗口,查看录制的视频后,点击【完成】,点击保存就可以完成了。这里注意了,录制时间是不限制的哦。 录屏流程都是非常简单而且非常方便的。电脑录屏怎么录?按照上面推荐的3个办法,只要打开了软件就可以直接录制,也可以选择数据蛙录屏软件自己进行安装,非常好用,操作简单又方便快捷!我们就按照上面所说的方法,这样就完成了录制电脑屏幕上啦!

QT编译Mysql驱动文件

一.错误分析 1.Qt报错:QSqlDatabase: QMYSQL driver not loaded 2.原因分析:QT没有MySQL的驱动,即在QT目录下没有qsqlmysql.dll文件。 3.解决方法:需要在QT中重新编译一下MySQL的驱动,得到qsqlmysql.dll文件,并将它放到QT合适的目录下。 二.软件版本 1.Qt:Qt5.15.2 2.MySQL:MySQL Server 8.0 三.解决步骤 1.通过Qt安装目录下的源码编译qsqlmysql.dll文件 (1).在Qt安装目录下的src源码里找到MySQL文件夹:D:\programming\Qt5.15\Install\5.15.2\Src\qtbase\src\plugins\sqldrivers\mysql (2).用Qt Creator打开mysql.pro文件 (3).注释掉 QMAKE_USE += mysql (4).添加以下代码 INCLUDEPATH += "C:/Program Files/MySQL/MySQL Server 8.0/include" DEPENDPATH += "C:/Program Files/MySQL/MySQL Server 8.0/include" LIBS += "C:/Program Files/MySQL/MySQL Server 8.0/lib/libmysql.lib" 其中的路径为自己MySQL安装的路径,需要根据安装位置做出更改 最后添加编译出来的MySQL驱动文件存放位置的代码 DESTDIR = ../mysql/mylib 点击构建,不出意外的话会报错:Cannot read D:/qtsqldrivers-config.pri: No such file or directory 这是因为找不到qtsqldrivers-config.pri文件,但是我们在sqldrivers文件夹下可以找到此文件,因此我们需要修改qtsqldrivers-config.pri文件。 在D:\programming\Qt5.15\Install\5.15.2\Src\qtbase\src\plugins\sqldrivers目录下打开qtsqldrivers-config.pri文件,把其中的 include($$shadowed($$PWD)/qtsqldrivers-config.pri) 注释掉 并添加这一句代码: include(./configure.pri) 完成以上全部操作后,再次点击小锤子构建项目。 打开mylib文件夹,里面就会有生成出来的适用于当前Qt版本的MySQL驱动文件了。 2.将对应编译套件的驱动文件放到对应的文件夹下 这里使用的是MSVC2019 64bit套件,则需要将驱动文件中的qsqlmysql.dll文件放入D:\programming\Qt5.15\Install\5.15.2\msvc2019_64\plugins\sqldrivers文件夹下。 3.将MySQL安装目录下的libmysql.dll文件放入合适文件夹 将MySQL安装目录下lib文件夹里的” libmysql.dll”文件复制到D:\programming\Qt5.15\Install\5.15.2\msvc2019_64\bin文件夹。 完成上述步骤就可以使用Qt连接MySQL数据库了。

基于VSCode和CMake的C/C++开发(Linux)1

基本的Linux指令 ctrl+alt+t 打开终端 完整指令的标准格式:命令(空格) [选项](空格) [操作对象] 选项和操作对象可有可无 pwd 打印当前目录ls 列出当前目录下所有文件和文件夹的名称ls [路径] 列出指定路径下文件和文件夹的名称 # ls 相对路径 ls ./ #【表示当前目录下】 ls ../ #【上一级目录下】 # ls 绝对路径 ls /home ls [选项] [路径] 含义:列出指定路径下的所有文件/文件夹的名称 绝对路径:相对根目录的路径; 相对路径:相对当前目录的路径; # ls 选项 路径 ls -lah /home # 选项解释: -l:表示list,表示以详细列表的形式进行展示 -a:表示显示所有的文件/文件夹(包含了隐藏文件/文件夹) -h:表示以可读性较高的形式显示 # ls -l 中 “-”表示改行对应的文档类型为文件,“d”表示文档类型为文件夹。 # 在Linux中隐藏文档一般都是以“.”开头 ctrl + l 清屏cd - change directory 作用:切换当前的工作目录 用法1:cd ; cd ~ 用法2:cd [相对路径] # 进入到上级目录下 cd ..

C# 下调试陷阱/调整权限/剥离调试器/调试自己

using System; using System.ComponentModel; using System.Runtime.InteropServices; public partial class ForciblyAdjustPrivilege { private abstract class NativeMethods { [DllImport("ntdll.dll", SetLastError = true)] public static unsafe extern int RtlAdjustPrivilege(int Privilege, [MarshalAs(UnmanagedType.Bool)]bool Enable, int CurrentThread, bool* Enabled); [DllImport("ntdll.dll", SetLastError = true)] public static unsafe extern int NtSetInformationThread(IntPtr ThreadHandle, int ThreadInformationClass, int ThreadInformation, int ThreadInformationLength); [DllImport("kerenl32.dll", SetLastError = true)] public static unsafe extern IntPtr GetCurrentThread(); [DllImport("kerenl32.dll", SetLastError = true)] public static unsafe extern IntPtr GetCurrentProcess(); [DllImport("

hive数据倾斜(超详细)

说到hive的数据倾斜,可能有的小伙伴还不了解什么是数据倾斜,所以咱们这一次就从hive数据倾斜的表现、hive数据倾斜发生的原因、hive数据倾斜的解决方案这三个方面来聊一聊hive的数据倾斜 1、hive数据倾斜的表现 我们都知道hive的底层其实是mr(MapReduce)引擎,hsql其实就是把sql语言转换成mr去运行,这样就大大缩减了咱们去写mr的时间,然而有时候咱们会发现在你运行一个任务的时候,明明所有的map task都完成了,并且99%的reduce task也完成,只剩下一个后者少数几个reduce task一直在执行,等了半天就是不动,其实这种情况一般都是发生了数据倾斜,说白了,hive的数据倾斜本质上就是MapReduce的数据倾斜(感兴趣的小伙伴可以去查看任务监控页面) 2、hive数据倾斜的原因 其实数据倾斜这个问题,在MapReduce编程模型中十分常见,根本原因就是大量相同的key被分配到一个reduce里,造成一个reduce任务累死了,但是其他的reduce任务闲死(和我现在上班一样,,,,)。下面咱们来罗列一下常见的数据倾斜有哪些原因: 一、key分布不均衡 二、业务问题后者业务数据本身的问题,某些数据比较集中 三、建表的时候考虑不周 四、某些sql语句本身就有数据倾斜,例如: (1)大表join小表:其实小表的key集中,分发到某一个或者几个reduce上的数据远远高于平均值 (2)大表join大表:空值或无意义值:如果缺失的项很多,在做join时这些空值就会非常集中,拖累进度。 (3)group by: group by的时候维度过小,某值的数量过多,处理某值的reduce非常耗时间。 (4)Count distinct:某特殊值过多,处理此特殊值的reduce耗时。 3、Hive数据倾斜解决 【参数调节】 hive.map.aggr = true Map端部分聚合。 有数据倾斜的时候进行负载均衡,当选项设定为true,生成的查询计划会有两个MRJob。第一个MRJob中,Map的输出结果集合会随机分不到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同于Group By Key 有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MRJob再根据预处理的数据结果按照Group By Key分不到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。 【SQL调整】 1)如何join:关于驱动表的选取,选用join key分布最均匀的表作为驱动表,做好列裁剪和filter操作,以达到两表做join的时候,数据量相对变小的效果。 2)大小表join的时候:使用map join 让小的维度表先进内存,在map端完成reduce。效率很高。 3)大表join大表的时候:把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后不影响最终的结果。 4)count distinct 大量相同特殊值,将这些值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1即可。如果还有其他的计算,需要进行group by,可以先将那些值为空的记录单独处理,再和其他计算结果进行 union。 5)group by 维度过小的时候:采用sum() group by 的方法来替换count(distinct)完成计算。 6)单独处理倾斜key:一般来讲倾斜的key都很少,我们可以将它们抽样出来,对应的行单独存入临时表中,然后打上随机数前缀,最后再进行聚合。或者是先对key做一层hash,先将数据随机打散让它的并行度变大,再汇集。其实办法一样。

一种裁剪人体步态轮廓图方法

写在前面 当我们好不容易从视频或者摄像头获取到了想要的步态轮廓图,却发现无法投入神经网络训练,这是为什么呢?其实是因为像GaitSet等步态识别网络对输入图像有标准的格式:64×44且裁剪过的人体步态轮廓图,我们只有满足这样的格式才能够投入训练。 而本文章主要就是提供一个方式可以将获得的步态轮廓图进行裁剪,使得其符合神经网络的输入格式,可以投入训练。 一种裁剪人体步态轮廓图方法 写在前面 实现code 核心代码: 枝干代码: 使用教程: 回顾: 实现code 使用这个函数我们文件夹下的图片需要满足的格式是类似这样子的二值图: 函数的原理其实就是找到图中白色像素的最高点、最低点、最右边的点和最左边的点,所以我们要确保图中仅仅出现了白色的人。 简单来讲就是需要图片是二值图的步态轮廓图! 核心代码: def cut(image): ''' 通过找到人的最小最大高度与宽度把人的轮廓分割出来,、 因为原始轮廓图为二值图,因此头顶为将二值图像列相加后,形成一列后第一个像素值不为0的索引。 同理脚底为形成一列后最后一个像素值不为0的索引。 人的宽度也同理。 :param image: 需要裁剪的图片 N*M的矩阵 :return: temp: 裁剪后的图片 size*size的矩阵。flag:是否是符合要求的图片 ''' image = np.array(image) # 找到人的最小最大高度与宽度 height_min = (image.sum(axis=1) != 0).argmax() height_max = ((image.sum(axis=1) != 0).cumsum()).argmax() width_min = (image.sum(axis=0) != 0).argmax() width_max = ((image.sum(axis=0) != 0).cumsum()).argmax() head_top = image[height_min, :].argmax() # 设置切割后图片的大小,为size*size,因为人的高一般都会大于宽 size = height_max - height_min temp = np.

Java IP归属地查询(离线方式+在线方式,内附查询IP方法)

一、离线方式 1.1. 下载 ip2region.xdb GitHub项目地址:https://github.com/lionsoul2014/ip2region 我们首先需要下载一个 ip2region.xdb 的文件 下载地址:https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb 打开后点击如图的Download图标即可下载。 下载完成后,需要将该文件放到我们的项目中。 ps:我是直接放到服务器的,因为放在项目的资源文件夹下,当我们调试的时候使用Java Spring自带的工具去获取该文件的绝对路径时,没有任何问题,能够正常获取到。但是当我们打成jar包部署到测试或者开发环境,就会拿不到该文件,具体情况可以看 spring boot集成,db放在resource下,打jar包后可以加载资源,但search不生效。本地debug生效 该Issues,如果不想麻烦的话直接放在服务器上吧,这样我们就能直接配好绝对路径了,少走弯路。 1.2. 代码实现 首先需要导包: <dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2region</artifactId> <version>2.6.5</version> </dependency> /** * 通过ip地址获取所属国家-离线方式 * * @param ip IP地址 * @param dbPath ip2region.xdb路径 * @return 国家信息 */ public static String getCountryByIpOffline(String ip, String dbPath) throws Exception{ // 1、从 dbPath 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。 byte[] vIndex = Searcher.loadVectorIndexFromFile(dbPath); // 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。 Searcher searcher = Searcher.newWithVectorIndex(dbPath, vIndex); // 3、查询 return searcher.

Dubbo与SpringCloud和Feign的区别

目录 1.SpringCloud与Dubbo的区别 2.注册服务的区别 3.Dubbo和Feign远程调用的区别 4.Rest和RPC对比 5.Eureka与Nacos注册中心的区别 6.Nacos中的CAP模式切换 7.Eureka和Zookeeper注册中心的区别 8.微服务之间是如何独立通讯的 9.微服务的优缺点 10. SpringCloud中的常用组件有哪些 11.总结 1.SpringCloud与Dubbo的区别 SpringCloud和Dubbo都是现在主流的微服务架构; SpringCloud是Apache旗下的Spring体系下的微服务解决方案Dubbo是阿里系的分布式服务治理框架 初始定位不同:SpringCloud定位为微服务架构下的一站式解决方案;Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用和治理 生态环境不同:SpringCloud依托于Spring平台,具备更加完善的生态体系;而Dubbo一开始只是做RPC远程调用,生态相对匮乏,现在逐渐丰富起来。 调用方式:SpringCloud是采用Http协议做远程调用,接口一般是Rest风格,比较灵活;Dubbo是采用Dubbo协议,接口一般是Java的Service接口,格式固定。但调用时采用Netty的NIO方式,性能较好。 组件差异比较多,例如SpringCloud注册中心一般用Eureka,而Dubbo用的是Zookeeper 两者的生态对比: 2.注册服务的区别 Dubbo是基于java接口及Hession2序列化的来实现传输的,Provider对外暴露接口,Consumer根据接口的规则调用。也就是Provider向Zookeeper注册的是接口信息,Consumer从Zookeeper发现的是接口的信息,通过接口的name,group,version来匹配调用。Consumer只关注接口是否匹配,而对此接口属于什么应用不关心。当然接口的注册信息里会包含应用的ip,hostname等。SpringCloud的服务发现是基于Http协议来实现的,Provider对外暴露的是应用信息,比如应用名称,ip地址等等,Consumer发现的是应用的信息,当调用的时候随机选择一个Provider的IP地址,应用名称,然后依据Http协议发送请求。Consumer关注的是应用名称,根据应用名称来决定调用的是哪个服务集群,然后对此名称对应的服务集群做负载均衡。Provider接受到请求后,根据内置的SpringMVC来匹配路由处理请求。 3.Dubbo和Feign远程调用的区别 Dubbo与 Feign 都依赖注册中心、负载均衡,作用是提供远程接口调用。 常见的 实现远程调用的方式: Http接口(web接口、RestTemplate+Okhttp)、Feign、RPC调用(Dubbo、Socket编程)、Webservice... 1、协议 Dubbo: 支持多传输协议(Dubbo、Rmi、http、redis等等),可以根据业务场景选择最佳的方式。非常灵活。 默认的Dubbo协议:利用Netty,TCP传输,单一、异步、长连接,适合数据量小、高并发和服务提供者远远少于消费者的场景。 Feign: 基于Http传输协议,短连接,不适合高并发的访问。 2、负载均衡 Dubbo: 支持4种算法(随机、轮询、活跃度、Hash一致性),而且算法里面引入权重的概念。 配置的形式不仅支持代码配置,还支持Dubbo控制台灵活动态配置。 负载均衡的算法可以精准到某个服务接口的某个方法。 Feign: 只支持N种策略:轮询、随机、ResponseTime加权。 负载均衡算法是Client级别的。 Nacos注册中心很好的兼容了Feign,Feign默认集成了Ribbon,所以在Nacos下使用Fegin默认就实现了负载均衡的效果。 3、容错策略 Dubbo: 支持多种容错策略:failover、failfast、brodecast、forking等,也引入了retry次数、timeout等配置参数。 Feign: 利用熔断机制来实现容错的,处理的方式不一样。 Feign是SpringCloud中的远程调用方式,基于成熟Http协议,所有接口都采用Rest风格。因此接口规范更统一,而且只要符合规范,实现接口的微服务可以采用任意语言或技术开发。但受限于http协议本身的特点,请求和响应格式臃肿,其通信效率相对会差一些。 Dubbo框架默认采用Dubbo自定义通信协议,与Http协议一样底层都是TCP通信。但是Dubbo协议自定义了Java数据序列化和反序列化方式、数据传输格式,因此Dubbo在数据传输性能上会比Http协议要好一些。 不过这种性能差异除非是达极高的并发量级,否则无需过多考虑。 4.Rest和RPC对比 Dubbo采用自定义的Dubbo协议实现远程通信,是一种典型的RPC调用方案,而SpringCloud中使用的Feign是基于Rest风格的调用方式。 1)Rest风格 REST是一种架构风格,指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。 Rest的风格可以完全通过HTTP协议实现,使用 HTTP 协议处理数据通信。REST架构对资源的操作包括获取、创建、修改和删除资源的操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法。 因此请求和想要过程只要遵循http协议即可,更加灵活 SpringCloud中的Feign就是Rest风格的调用方式。 2)RPC Remote Procedure Call,远程过程调用,就是像调用本地方法一样调用远程方法。

【CSS】使元素在父元素中居中显示的几种方法

在页面元素布局时经常会有把元素居中的需求,大多都是用弹性盒或者定位,下面来说一下使用方法 一、使用边距进行固定位置 这种方法需要把父元素和子元素的宽度固定,然后利用二者宽高之差添加边距移动元素的位置 现在创建了一个父元素box1中包含了一个子元素box2,下边的许多案例都会使用这个盒子样式 <div class="box1"> <div class="box2"></div> </div> .box1{ width: 500px; height: 500px; background-color: #f00; } .box2{ width: 100px; height: 100px; background-color: #0f0; } 子元素默认在父元素的左上角位置,因为已经知道父元素和子元素的宽高,因此可以使用二者之差的二分之一作为边距宽度即可 500px - 100px = 400 ⇒ 400px / 2 = 200px 1、给父元素添加内边距 在元素添加padding值之后盒子的尺寸会加上内边距导致尺寸变大 如果不希望外边距影响父元素宽高需要添加 box-sizing: border-box; 关于box-sizing .box1{ width: 500px; height: 500px; background-color: #f00; padding:200px //使用内边距将四周距离全部占满,使子元素被挤在中间 box-sizing: border-box //将宽高变为盒子宽高而不是内容区宽高 } 2、给子元素添加外边距 直接给子元素添加外边距margin值即可,效果与上边一样,无需修改box-sizing .box2{ margin:200px } 此时父元素剩余空间被子元素的外边距占满 二、使用绝对定位 使用绝对定位的方法可以在父元素与子元素宽高不固定的情况下进行定位,使元素进行居中 元素开启绝对定位之后可以脱离文档流,因此元素居中后会浮在父元素中间 使用绝对定位进行元素居中有两种方法 1、添加 x轴 和 y轴 偏移量后往回移动 <div class="

kafka --- 常见性问题

数据丢失和如何保障数据不丢失 数据丢失的情况: producer端 1、设置acks=all acks=0:producer把消息发送出去了,就确认发送成功了,但是如果此时leader分区宕机了,根本没有接收这条消息,或者还没有写入日志,导致数据丢失 acks=1:producer把消息发送出去了,leader分区收到并写入日志,,就确认发送成功了,但是如果此时leader分区宕机了,根本没有把这条消息同步给follower分区,follower分区后续继续选举了一个新的leader分区,新leader分区并没有这条新数据,导致数据丢失 acks=all:当leader分区收到并写入日志,及同步给follower分区之后,再确认消息发送成功 2、producer: 异步批量发送 一般情况下,为了提高吞吐量,发消息会实行异步批量发送, 这样做有一个风险,当buffer里面有很多数据的时候,此时producer宕机了,会把buffer里面的数据给丢失了,导致数据丢失 保障不丢失措施: 1、把异步改成同步 kafkaTemplate.send("testTopic",gson.toJson(message)); 改成: Future future = kafkaTemplate.send("testTopic",gson.toJson(message)); future.get(); 2、不要使用producer.send(msg),而要使用producer.send(msg, callback)。在callback回调函数 中可以把发送失败的消息给保存下来,进行下一次发送 3、设置retries ,当网络发生抖动,配置了retries > 0的producer能够自动重试消息发送,避免消息丢失。 broker端: 设置副本数>1,如果副本数=1,则就算acks=all ,也可以会造成数据丢失unclean.leader.election.enable 设置为 true,选举从ISR列表中选举 comsumer端: 自动提交是当消费者poll过来了,就自动提交offset给broker,如果此时消费者接收到了数据,并没有做处理的时候,突然消费者当前的机器宕机了,导致还没对这条数据做处理(保存数据库或者其他计算),最终丢失了 1、设置enable.auto.commit=false 在处理完这条数据后,手动提交offset 2、设置auto-offset-reset 为 earliest 这个参数指定了在没有偏移量可提交时(比如消费者第l次启动时)或者请求的偏移量在broker上不存在时(比如数据被删了),消费者会做些什么。 这个参数有两种配置。一种是earliest:消费者会从分区的开始位置读取数据,不管偏移量是否有效,这样会导致消费者读取大量的重复数据,但可以保证最少的数据丢失。一种是latest(默认),如果选择了这种配置, 消费者会从分区的末尾开始读取数据,这样可以减少重复处理消息,但很有可能会错过一些消息。 消息的有序性 1、kafka的特性是只支持partition有序,之前在生产者讲分区选择的时候,可以指定key的方式发送数据 ,可以用业务id,作为key,达到业务上的有序 2、如果业务需要部分数据有序,除了只建立一个分区外,则可以在消费端添加缓存,根据消息先后顺序的字段进行判断,如果A,B两条数据,如果非要B在A之后执行,则如果先pull消息B,则业务逻辑判断,A是否被消费,如果未被消费,则把B放进缓存,则pull消息A过来的时候,消费去缓存中看有没有B,如有,则消费B 重复消费 1、 比如消费者手动提交的时候,处理完这条消息,但是在进行手动提交的时候,突然宕机了,并没有修改broker中的offset值 2、当有人估计把broker中保存offset的topic给删掉的时候,我们没法知道当前分区的offset 是什么,只有从头再读一遍,会造成大量重复消息 3、在自动提交情况下,消费者提交offset不是消费一条就进行提交的,而是按一定的时间周期进行提交的(auto_commit_interval_ms 参数进行设置),如果正好在这个时间之内,服务器宕机了,就导致offset没有更新,当消费者启动的时候,会重复消费之前的数据 解决办法是: 1、本地保存处理完消息的offset 值,每条消息都带有一个offset,在表设计的时候,可以topic 分区 offset三维度区设计 2、消费数据的幂等性,可以定义消息的唯一属性,如果数据库存在唯一性的消息就不进行处理或者更新处理 消息堆积 看查看消费组的消息堆积情况命令 修改副本数 在server.properties中配置: # 自动创建主题 auto.create.topics.enable=true # 默认主题的分区数 num.partitions=3 # 默认分区副本 default.

python学习——turtle

本文的所有源码来自于:https://github.com/jackfrued/Python-100-Days/tree/master/Day01-15 文章目录 一、概述:二、安装三、基础命令介绍 一、概述: turtle(海龟)库是turtle绘图体系python的实现; turtle绘图体系:1969年诞生,主要用于程序设计入门; turtle库是python的标准库之一;属于入门级的图形绘制函数库; 二、安装 直接使用pip进行安装 pip install turtle 三、基础命令介绍 画布 画布cancas是绘图区域,可以设置它的大小和初始位置 turtle.screensize(1000,600,'red') #大小的设置 turtle.setup(width=0.5, height=0.75, startx=None, starty=None)#参数: #width, height: 输入宽和高为整数时, 表示像素; 为小数时, 表示占据电脑屏幕的比例, #startx, starty: 这一坐标表示矩形窗口左上角顶点的位置, 如果为空,则窗口位于屏幕中心。 显示绘图窗口 turtle.mainloop()#必须是程序的最后一个语句 画笔 (1)画笔运动的命令 turtle.forward(distance) #向当前画笔方向移动a像素长度 turtle.backward(distance) #向当前画笔相反方向移动a像素长度 turtle.right(degree) #顺时针移动 aturtle.left(degree) #逆时针移动 aturtle.pendown() #移动时绘制图形 turtle.goto(x,y) #将画笔移动到坐标为x,y的位置 turtle.penup() #移动时不绘制图形,提起笔 turtle.speed() #画笔绘制的速度范围 turtle.circle() #画图,半径为正,表示圆心在画笔的左边画圈 (2)画笔控制命令 turtle.pensize(width) #绘制图形的宽度 turtle.pencolor() #画笔的颜色 turtle.fillcolor(color) #绘制图形的填充颜色 turtle.color(color1,color2) #同时设置pencolor=color1,fillcolor=color2 turtle.filling() #返回当前是否在填充状态 turtle.begin_fill() #准备开始填充图形 turtle.end_fill() #填充完成 turtle.hideturtle() #隐藏箭头显示 turtle.

Vue2和Vue3的响应式原理及区别

响应式数据的声明。以下内容帮助理解,不建议死记硬背 1、在Vue2中,只要是在data里面定义的数据,就能在模板中使用,且它是一个响应式数据。数据进行修改,模板就会进行修改。但是其不能监听到深层对象数据和数组数据的更改。Vue2是通过this.$set和this.$delete两个api去解决的 比如this.$set(this.person,“sex”,“女”)是给person对象添加一个sex为女的响应式属性 this.$delete(this.person,name)是动态的删除person对象的name属性 2、Vue2的双向数据绑定是利用了es5 的一个API Object.definepropert() 对数据进行劫持 结合发布订阅模式来实现的。Vue3中使用了es6的proxy对数据进行处理。 3、在Vue3中,ref函数常用于将简单数据类型定义为响应式数据。relactive函数常用于声明复杂数据类型为响应式 reactive函数里面写了具体对proxy的实现 ref处理基本数据类型,底层用的是objectdefineproperty,用的是getter和setter,但是处理对象数据其底层还是求助了reactive 。是通过proxy来实现的 ref既可以声明简单数据类型也可以声明复杂数据类型,relative只能声明复杂数据类型 4、取值:在setup里面再次获ref声明的数据的值时,需要.value;在模板中使用ref声明的响应式数据,可以省略.value(因为模板在解析的时候会判断,数据是否是ref声明的数据,如果是,会直接取出里面value的值) 5、Vue3下响应式原理如下 其底层对数据的增删改查是通过Proxy和Reflect去处理的(代理和反射) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> let person = { name:'张三', age:18 } const p = new Proxy(person,{ //读取的对象和属性 get(target,propName){ console.log(`有人读取了p身上的${propName}属性`); //Reflect(反射)读取对象身上的属性 return Reflect.get(target,propName); }, //修改或新增的对象,修改或新增的属性,修改或新增后的值 set(target,propName,value){ //下面的p即为target console.log(`有人修改了p身上的${propName}属性`); //修改和新增对象身上的属性 return Reflect.set(target,propName,value); }, //删除的对象和属性 deleteProperty(target,propName){ console.log(`有人删除了p身上的${propName}属性`); //删除对象身上的属性 return Reflect.deleteProperty(target,propName); } }) </script> </body> </html> 运行上面的代码,就可以在浏览器的控制台进行验证

K近邻算法实例

一、实验目的 1、理解K近邻算法中距离的度量、K值得选取和分类决策规则的确定; 2、掌握用K近邻模型对所给的实例进行正确分类的方法; 3、掌握K近邻相关数据结构并能熟练运用Python编写程序。 二、实验要求 1、读代码,学python。要求能读懂所给代码,能改写代码实现其它类似算法 2、运行结果要截图并对结果做分析; 3、所有内容汇总到本文档,无需提供.py文件; 三、实验内容 阅读代码并进行注释用KNN算法改进约会网站的配对效果 海伦女士一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的任选,但她并不是喜欢每一个人。经过一番总结,她发现自己交往过的人可以进行如下分类:不喜欢的人、魅力一般的人 、极具魅力的人 海伦收集约会数据已经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,每个样本数据占据一行,总共有1000行。 海伦收集的样本数据主要包含以下3种特征:每年获得的飞行常客里程数、玩视频游戏所消耗时间百分比、每周消费的冰淇淋公升数 将数据集的部分当作训练集,其余作为测试集,通过KNN算法进行分类预测,将预测结果输出并与真实结果对比,计算分类错误率。 四、实验过程(原理,源码) KNN算法理解: KNN 算法采用测量不同特征之间的距离方法进行分类,通俗的讲就是:给定一个样本数据集,并且样本集中每个数据都存在标签,即我们知道样本集中每个数据与所属分类的对应关系。对新输入没有标签的实例,在训练数据集中找到与该实例最邻近的 k 个实例,这 k 个实例的多数属于某个类,就把该输入实例分为这个类。 1.阅读并对代码进行注释 要实现手写数字识别,首先需要将二进制图像转化为向量,通过构造一个函数,将3232的01串连接起来,输出11204的向量。然后通过KNN算法,计算训练集元素之间的距离并进行分类。最后是将手写数字识别的训练集和测试集进行导入,将预测结果输出并与真实结果对比,算出分类的错误率。 通过对代码按照上述思路进行分析并添加注释: 首先是构造一个函数将3232转换为11024的向量 """ img2vector 函数说明:将32*32的二进制图像矩阵转换成1*1024的向量 Parameters: filename - 二进制图像文件名 Returns: returnVect - 1*1024的向量 """ def img2vector(filename): returnVect = np.zeros((1, 1024)) #zeros(shape, dtype=float, order='C') 返回一个给定形状和类型的用0填充的数组; fr = open(filename) #读取二进制图像文件 for i in range(32): #32行 lineStr = fr.readline() for j in range(32): #32列 returnVect[0, 32*i+j] = int(lineStr[j]) return returnVect #返回1*1024的向量 下面是KNN算法函数,通过输入训练集数据、分类标准、和K值,返回分类结果

threejs-场景创建(基于react-hooks)

个基本的threejs程序,主要内容如下: 一个基本的threejs程序,创建场景,一个兰伯特材质地板创建MeshLambertMaterial材质的球体和立方体并对他们添加阴影为场景添加鼠标并使得页面大小自适应屏幕为立方体添加旋转动画效果为球体添加弹跳动画效果 效果如图 代码: import { useRef, useEffect, useCallback } from 'react' import * as THREE from 'three' import OrbitControls from 'three-orbitcontrols'; import './index.scss' const App = () => { const page = useRef(); // useRef不会导致重新渲染 /** * 场景、相机、渲染器作为threejs的基本结构,需要在页面进入时渲染完毕 */ const scence = useRef(new THREE.Scene()).current; //场景 const camera = useRef(new THREE.PerspectiveCamera()).current; //摄像机(透视投影) const render = useRef(new THREE.WebGLRenderer()).current; //渲染器 const meshList = useRef([]).current // 模型容器 const timer = useRef(null) // 定时器 const controls = new OrbitControls( camera, render.

defer-promise 源码解析(Npm library)

defer-promise 源码解析(Npm library) 文章目录 defer-promise 源码解析(Npm library)基本信息源码解析使用参考链接 基本信息 version:v3.0.0功能:利用 Promise 的特性实现两块异步代码同步化传送门:75lb/defer-promise - Github 源码解析 我们先来看看源码,非常简短 /** * @module defer-promise */ function defer () { if (typeof Promise !== 'undefined' && Promise.defer) { return Promise.defer() } else { var deferred = {} deferred.promise = new Promise(function (resolve, reject) { deferred.resolve = resolve deferred.reject = reject }) return deferred } } export default defer 先构建一个 Promise 对象,然后将 resolve、reject 方法透出并返回,如此一来我们就可以如下去使用 使用 let solver; function invoker() { solver = defer(); // start task // .

C语言——折半查找法

一、使用场景 假如现在有一组数据,你想要查询这个具体某一个数据在这一堆数据中的所在位置,这个时候就需要程序在这一组数据中,找到与想要查找的目标数据相匹配的那个数据,然后返回相对应的位置。如果将问题再细化简化一点,假如现在有一组有顺序的数字,需要你编写程序找到其中一个数字所在的位置。了解需求之后,我们脑海中一般首先浮现的思路便是,编写一个数组,然后将数字一个个进行匹配,最后找到这个数字的位置,返回该位置,问题解决。 现在我们就将此思路实践一下看看是否能够找到该数字。给出一个包含十个元素的数组,里面包含了1,2,3,4,5,6,7,8,9,10,然后尝试在其中找到数字7对应的位置,代码如下所示: 上面的代码使用了flag作为一个标志数,因为成功找到后break跳出循环继续执行,但是如果查找的是一个不存在该数组中的数字时,对上面数据全部进行查找之后也会结束循环执行,此时如果没有标志数,程序就无法判断是因为哪种原因跳出了循环结构。所以定义一个变量flag进行判断:如果成功找到,flag的值为1,否则为0,程序就能通过if语句进行判断是哪种情况,判断是否输出找不到情况下的语句。 我们发现最后是可以实现这个目标的,成功找到了数字7在这个数字中对应的位置:7.(这个位置是指在数字的位置而不是数组中的下标)。 但是这样一个个数字进行匹配,虽然能找到对应位置,但是假如现在有100或者1000甚至10000个数字,然后想要找到比较靠后的一个数字对应的位置时,这种方法就显得有些许“笨拙”了,不够灵活和简便,要将所有数字从头到尾一个个进行匹配,直至找到为止。此时就需要使用一个更加简便更加快速的方法——折半查找法,或者叫做二分查找法。 二、如何实现折半查找法 假如一天你的同学买了一双球鞋,只告诉你这双鞋在一千以内,然后让你猜这双鞋价格是多少,你会怎么做?可能有少部分人会上来就给这位朋友一拳,然后一顿输出“你买鞋又不是买给我,还**让我猜价钱,凡尔赛你**,我******”,但是大多数人还是会选择正确的做法:从中间数500开始猜,而不是像上面的做法一样,真的就傻傻的从1,2,3开始猜到1000。所以折半查找法的思想也是如此。 我们从这一组有序数字中,取其中位数与我们查找的目标数进行匹配,如果正好一发入魂,那就直接找到了该数,但是即使大概率不正确,我们也可以直接干掉一半的数据。举个例子:假如现在有个数组是1-1000,目标数是777,那么我中位数是500,进行比较之后是比目标数更小,那就说明目标数的位置只可能在中位数往上,而不能在500之下,那么500以下的数字就全部被干倒了,这一次查找就可以消灭掉一半数据,而上面的做法一次只能消灭一个数字,效率相比之下就产生了很明显的差距了。然后第一次查找后再进行第二次折半,取750进行比较,这次还是比目标数小,但是仍然干掉了一半数据,依次进行下去,只需使用少数次数查找便可以从大量数据中找到。 现在还是使用上面的例子,示例以下折半查找法: 上面代码中先定义了一个左下标和右下标,从而定义出中位数的下标。每次查找之后,如果目标数 > 中位数,那么目标数只会出现在中位数往上的位置,那么此时右下标不需变动,左下标应该是中位数的下标 +1,再次对左右下标相加除以2,取得剩下数据的中位数作为新的中位数进行下一次查找。如果目标数 < 中位数,那么目标数只会出现在中位数之下的位置,那么此时左下标不需变动,右下标应该是中位数的下标 -1,然后再次取新的中位数,以此类推进行查找,所以使用while循环。 第一次查找的图示如下,第一次查找之后可知 k > 中位数,所以 k 的位置只可能出现在中位数的右边,此时我们的第二次查找想要从中位数的右边第一个数开始找起,所以此时左下标应该是 mid + 1。 所以第二次查找如下,可知现在 k < 中位数,所以只可能出现在中位数的左边,此时我们希望下次查找从中位数的左边第一个数开始,所以此时右下标为 mid - 1。 然后后续查找都如上图所示,直至左右下标交错(left > right)或者相等(left = right),便是循环结束的标志。 三、总结 折半查找法每一次查找都可以判断一半数量的数据是否匹配,效率比第一种方法高效很多。k = 7 这种情况是查找次数最多的情况,都只需要四次查找即可完成,但是第一种方法查找需要七次,相比之下,足以见得折半查找法的高效性。 但是折半查找法也是需要前提条件才可以使用的,就是要求查找的这组数据必须是有序的,倒序和顺序都可以。面对无序的数据,折半查找法就失效,这也是相较于第一种方法的不足之处,两种方法各有所长,须根据不同情况进行使用。

RxJS 实战: 基于 BehaviorSubject 实现状态管理 & 结合 React

RxJS 实战: 基于 BehaviorSubject 实现状态管理 & 结合 React 文章目录 RxJS 实战: 基于 BehaviorSubject 实现状态管理 & 结合 React完整代码示例基于 BehaviorSubject 定义状态自定义 HookReact-RxJS参考链接 完整代码示例 https://github.com/superfreeeee/Blog-code/tree/main/front_end/others/rxjs/rxjs_state_management_react 基于 BehaviorSubject 定义状态 上一篇我们提到 RxJS 里面的 BehaviorSubject 非常适合作为一个响应式对象来帮助我们进行状态管理。今天我们来尝试手写一下基于 React 框架的封装 /src/store/state.ts 一开始我们先来看我们可以利用 BehaviorSubject 做出哪些常用的状态 基础状态 export const nameSubject = new BehaviorSubject<string>('crystal'); export const ageSubject = new BehaviorSubject<number>(15); 导出量 也称为计算状态,类似 Vue 里面的 computed export const maxAgeSubject = ageSubject.pipe( map((age) => (age >= 18 ? 18 : age)), ); 依赖多个状态的导出量

Pytorch中卷积与池化等的实现以及模型搭建

在上一篇文章中已经介绍了Pytorch中Dataset类以及Transform类中一些方法的使用,接下来介绍利用Pytorch来实现卷积等操作的实现。 一、nn.Module类 一个nn.Module是神经网络的基本骨架,可以视为一个块。如果神经网络要重写初始方法,则必须要调用父类的初始化函数。 所有的module包含两个主要函数: init函数:在里边定义一些需要的类或参数。包括网络层。 forward函数:做最终的计算和输出,其形参就是模型(块)的输入。 现在来简单写一个类: class Test1(nn.Module): def __init__(self) -> None: super().__init__() def forward(self,input): output = input+1 return output 这个类的作用是传入一个数,输出这个数加一后的结果,调用类: test1 = Test1() x = torch.tensor(1.0) 注意:在调用forward方法时不用引用函数,因为集成的nn.Module中的forward方法是__call__()方法的实现,可调用对象会调用__call__()方法。 output = test1(x) print(output) 输出结果如下: 二、卷积 1、conv2d 卷积分为不同的层,如con1、con2等,以二层卷积为例,具体的参数可查看官方文档,卷积操作主要就是用卷积核(weight)与原始数据进行计算,再加上其他的操作,最后得到一个新的输出。。 其中一些重要参数的含义如下: output就是卷积神经网络模型计算后的输出。 input是输入的数据,在此为一个二维数组,代表一张图片。 kernel表示卷积核,同input形状,也是一个二维数组,并且两者的形状都要有四个指标,否则要进行reshape。 stride表示步长。 padding表示周围填充几层,填充的默认值是0。 首先导入包: import torch import torch.nn.functional as F 设置输入数据: input = torch.tensor([[1, 2, 0, 3, 1], [0, 1, 2, 3, 1], [1, 2, 1, 0, 0], [5, 2, 3, 1, 1], [2, 1, 0, 1, 1]]) 设置卷积核:

Docker与VMware冲突(关闭Hyper-V)

docker开启了就不能用VMware。 前天我电脑下载了docker,虽然不久后又卸掉了,但是第二天也就是昨天,我再次打开电脑,打开VMware的时候,发现弹出提示:此平台不支持虚拟化的intel vt-x/ept。 这时候就纳闷了,为啥?? 我查看了一下虚拟机的设置,发现我是开启虚拟化的intel vt-x/ept的,然后我就在网上搜索了一番,实践后成功解决,得到解决方法如下: 1. 关闭服务里的有关HV的全部服务 由于我在 控制面板--程序--Windows功能 里找不到Hyper-V的功能,所以我采用以上方法禁用Hyper-V。 2.以管理员身份运行 Windows PowerShell 运行命令:bcdedit /set hypervisorlaunchtype off 这样Hype-V就禁用彻底了。 接下来重启一下电脑,再打开虚拟机,成功开机运行啦! 解决方法参考博文:https://blog.csdn.net/CNjcdyl/article/details/124802614 当想要启动docker时,则把上述功能都开启: 1.运行命令:bcdedit /set hypervisorlaunchtype auto 2.启用“适用于 Linux 的 Windows 子系统”功能 可输入命令:dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 3.启用“虚拟机平台”功能 可输入命令:dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 再重启电脑,就可以了。

Jmeter5.x互联网大厂性能与压力测试常用技能概览【综合项目实战】

Jmeter在互联网大厂中,是开发和测试人员对系统进行性能测试和压力测试常用工具;开发人员在开发过程中需要对接口进行测试和真实数据的批量模拟压测等;测试人员需要使用Jmeter对系统接口进行性能与压力的测试,保障系统在项目前期特定配置下达到稳定和可用状态; 本次文章是日常实战中对Jmeter的运用,独立整理为一个专题栏目; 使用当前主流项目框架技术和常规压测流程,对系统进行逐步调参压测,带领你认知单机情况下,最高的QPS。以及如何使用Jmeter的HTML可视化报告向上级继续汇报。 在发现接口有问题有严重问题时,如何用有效的测试数据和开发沟通并提出优化建议;适合人群:初中高级测试人员,全栈工程师,高级开发工程师,架构师,技术leader Jmeter官方地址:Apache JMeter - Apache JMeter™ 【综合实战】模拟生产环境压测配置: 阿里云ECS服务器:4核8G;1台,用于发布压测项目; 阿里云ECS服务器:2核8G;1台,用于使用Jmeter进行内网压测; 数据库RDS:mysql8,2核8G;1台 压测项目:SpringBoot+MybaitsPlus+JWT,集成token授权机制,来真实达到生产环境下效果; 接口列表: 接口名称接口描述用户登录模拟管理员登录,登录成功返回token,其他基础接口需用到会员列表分页获取会员列表,需要管理员token新增会员新增会员信息,需要管理员token修改会员根据会员编号修改会员基础信息,需要管理员token获取单个会员根据会员编号获取会员信息,需要管理员token删除会员根据会员编号,删除指定会员,需要管理员token新增商品新商品信息,需要token获取会员下单token模拟会员登录返回token,后续下单接口需要使用下单接口模拟会员在商城进行下单,需要会员token才可以下单;主要进行会员信息,商品参数等校验,以及最后的下单到数据;压测主要接口订单列表分页查询订单列表,需要管理员token Jmeter汉化界面: 专题内容摘要: 1、掌握并实战运用HTTP协议包以及日常状态码,媒体类型区分; 2、Jmeter5.x多环境安装实战(win+Linux+Mac) 3、搭建测试接口环境并进行性能测试; 4、Jmeter生成聚合报告以及参数指标分析 5、集合测试环境进行断言编写实战; 6、CSV可变参数的标准测试流程 7、互联网大厂标准压测流程介绍与实战; 8、【高级进阶】Jmeter5.x性能测试之自定义BeanShell脚本实战(一) 9、【高级进阶】Jmeter5.x性能测试之BeanShell关联接口压测实战(二) 10、【高级进阶】Jmeter5.x性能测试之BeanShell引入外部Java文件实战(三)​​​​​​​ 11、【高级进阶】Jmeter性能测试之让测试数据说话 12、【高级进阶】Jmeter非GUI压测实战 13、【综合实战】电商项目环境搭建(Springboot+Mysql+MybaitsPlus) 14、【综合实战】电商项目接口压测实战 Jmeter压测环境数据可视化环境 Jmeter压测脚本结构 ​​​​​​​ 测试后台管理登录 压测数据可视化监控 会员可视化 商品可视化 订单可视化 后端部分代码 前端部分代码 项目服务器环境截图 Jmeter生成HTML可视化报告部分截图 后端接口源码地址:Jmeter5.x / Jmeter-shop-test · GitCode 前端项目源码地址:Jmeter5.x / Jmeter-shop-test-admin · GitCode 本文章持续更新中...

嵌入式51/52单片机——流水灯实验小白教程(详细完整过程)Proteus 8配合Keil5仿真

1. 双击打开Proteus 2. 打开下面显示的界面后,点击“新建工程” 3.跳出这个界面 4.修改名字为“流水灯”,点击浏览选择自己想放置的位置(根据个人需求,不想改也可以用上面的默认路径,记得就行) 5.点Next 6.点Next 7.点Next 8.点Finish 9. 新建工程成功 10.接下来绘制原理图,先看成品,接下来详细讲解电路绘制过程。 11.放置单片机(这里我用的是AT89C52,你也可以选你想用的单片机)。 先点击这个黄色三角形,元件模式。 然后点击P按钮→在keywords一栏输入关键字搜索器件→点击AT89C52(或者你想用的单片机)→确定 然后可以看到左上角的小框里出现了刚才选择的单片机,同时鼠标会变成黑色小铅笔 然后鼠标左键点击一下,出现单片机的紫色轮廓,此时移动鼠标可以移动单片机,选择一个你觉得合适的位置,再点击一下鼠标左键,就成功放置单片机了。 放置好单片机如下: 想移动单片机的话,可以右键器件,点击“移动对象”。 12. 绘制时钟电路。所需器件:两个30pF的电容(关键字:CAP),一个晶振(关键字:STAL),一个地(关键字:GROUND)。 按照上面的步骤分别放置好这些器件:P→输入keywords关键字→选择器件→鼠标左键点一下选择合适位置→鼠标左键再点一下放置器件。 ①电容(CAP): 第二个电容可以直接复制第一个电容得到:单击选中C1→复制→粘贴 记得修改两个电容的值:双击1nF→跳出编辑零件值窗口→在字符串一栏修改数值为30pF→确定 ②晶振(STAL): ③地(GROUND):先选终端模式,再选择器件。 ④连线:把鼠标放在器件的一端,会出现一个红色小方块,左键点击一下,然后找到你想连的器件输入或者输出端,放在上面时同样会出现红色小方块,左键点击一下,一条线就连好了。重复这个过程,连接成下面所示的时钟电路。 时钟电路: 13:绘制复位电路。 所需器件:一个电容,两个电阻,一个按键,一个地。 ①电容(CAP):注意选的是CAP-ELEC ②电阻(RES) ③按键(BUTTON): ④地:先选终端模式(那两个黄色反向箭头),再点击GROUND ⑤顺便再放一个VCC待会把EA管脚一起接了: ⑥按照下面的电路图连线,注意R9的阻值为330,你这个位置的电阻也要改成330。 14.绘制流水灯电路。 所需器件:8个LED灯,8个电阻(用于限流),一个VCC。 LED灯: 电阻和VCC前面说过了,不再赘述。 按照下面的电路连接: 15.到这里,流水灯原理图电路就算完成了。 16.接下来该写实现流水灯的代码了,这里用到的是 Keil uVision5 。 17.双击Keil uVision5进入。 18.新建工程:打开keil → project → New μVision Project 19.输入工程名称,点击保存,新建成功。 20.新建空白文档。 21.将下面的代码写进文档。 #include<reg52.h> void delay(unsigned int i); int main(void) { while(1) { P2=0xfe; //1111 1110 delay(50000); P2=0xfd; //1111 1101 delay(50000); P2=0xfb; //1111 1011 delay(50000); P2=0xf7; //1111 0111 delay(50000); P2=0xef; //1110 1111 delay(50000); P2=0xdf; //1101 1111 delay(50000); P2=0xbf; //1011 1111 delay(50000); P2=0x7f; //0111 1111 delay(50000); } } void delay(unsigned int i) //延时函数 { while(i--); } 21.