使用langchain4j调用大模型写个聊天助手

LangChain4j是一款基于Java的高效、灵活的AI大模型应用框架,专为简化Java应用程序与LLMs(大语言模型)的集成而设计。它提供统一API和模块化设计,支持多种LLM提供商和嵌入模型,以及丰富的工具箱,如AI服务和RAG(检索增强生成)。LangChain4j通过简化集成过程,降低开发成本,助力开发者快速构建和部署AI应用。langchain4j还提供了openAI部分接口免费测试的能力,可以在没有key的情况下学习使用大模型。

在这里插入图片描述
UI代码在文末

1、导入相关包

<!-- openai包 -->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId><version>0.32.0</version>
</dependency>
<!-- 高级工具包,如ai service -->
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId><version>0.32.0</version>
</dependency>
<!-- 日志工具 -->
<dependency><groupId>org.tinylog</groupId><artifactId>tinylog-impl</artifactId><version>2.6.2</version>
</dependency>
<dependency><groupId>org.tinylog</groupId><artifactId>slf4j-tinylog</artifactId><version>2.6.2</version>
</dependency>

langchain4j是一个还在完善的库,最新版本请查看官网: Get Started | LangChain4j

jdk版本最好使用17,因为它还有一个基于 Spring Boot 3.2 版本,它最低支持jdk17

2、helloword入门

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
....public static void main(String[] args) {ChatLanguageModel model = OpenAiChatModel.withApiKey("demo");String answer = model.generate("你是谁");System.out.println("answer:" + answer);
}

控制台会输出:

answer:我是一个智能助手,可以回答您的问题并提供帮助。您有什么需要我帮忙的吗?

代码里的 demo 是langchain4j给开发者免费使用可调用openai模型(gpt-3.5-turbo)的key,不要在生产环境使用哦。

如果引入的是智普包,那么使用ZhipuAiChatModel替换即可

如果你有自己的key,代码如下配置:

    public static void main(String[] args) {// 替换成你自己的URL和key就行ChatLanguageModel model = OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();String answer = model.generate("你是谁,我是小飞");System.out.println("answer:" + answer);String answer2 = model.generate("请问我是谁");System.out.println("answer2:" + answer2);}

控制台输出:

answer:很高兴认识你,小飞。我是一个智能助手,可以回答你的问题和提供帮助。有什么我可以帮助你的吗?
answer2:很抱歉,我无法知道您是谁,因为我是一个虚拟助手,无法直接获取您的身份信息。您可以介绍一下自己,让我更了解您吗?

为什么会这样呢?

由于大模型是无状态的,你要让它知道上下文信息,则需要把你们的会话历史记录也发给它,否则每次都是一次新的会话。langchain4j可以使用ChatMemory管理会话。

2、AiServices和ChatMessage

AiServices 封装了与 LLM 交互的复杂性,使得开发者能够以更自然、更面向对象的方式来与 LLMs 进行交互。

ChatMessage是对话中的一个消息的抽象表示,它包含了4个实现类:UserMessage(用户消息)、AiMessage(大模型回复消息)、SystemMessage(系统消息,如应用的角色和能力)、ToolExecutionResultMessage(用于调用本地应用,可扩展大模型能力)

package org.example;import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.*;public class Test {interface Writer{@SystemMessage("请扮演一名作家,根据输入的文章题目写一篇{{num}}字以内的作文")String write(@UserMessage String text, @V("num") int num);}public static void main(String[] args) {ChatLanguageModel chatLanguageModel = OpenAiChatModel.builder()              .baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();// 使用代理模式创建一个作家对象Writer writer = AiServices.create(Writer.class, chatLanguageModel);String content = writer.write("我的爸爸", 100);System.out.println(content);}
}

控制台输出:

我的爸爸是我生命中最重要的人。他不仅是我生活中的导师,还是我永远的支持者。每当我遇到困难时,他总是在我身边给予我鼓励和指导。他教会我坚强,教会我勇敢,也教会我如何面对生活的挑战。他是我心中的英雄,是我无法取代的存在。我爱我的爸爸,他是我生命中最坚定的依靠,也是我永远的骄傲。

3、使用ChatMemory管理上下文

3.1 两种ChatMemory的使用方式

ChatMemory主要用于管理和维护聊天过程中的消息记忆,使得大型语言模型(LLM)能够模拟记住对话上下文的能力

LangChain4j 提供了两种 ChatMemory 的实现方式,如MessageWindowChatMemory 和 TokenWindowChatMemory 。这些实现方式可以根据不同的需求进行选择:

  • MessageWindowChatMemory:保留最新的 N 条消息并删除旧消息。由于每条消息可以包含不同数量的令牌,因此这种实现方式主要用于快速原型设计。
  • TokenWindowChatMemory:保留最新的 N 个令牌,并根据配置的token删除较旧的消息,能够更精确地控制上下文窗口的大小。
package org.example;import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.*;public class Test2 {interface NamingMaster {String talk(String desc);}public static void main(String[] args) {ChatLanguageModel model = OpenAiChatModel.builder().apiKey("demo").baseUrl("http://langchain4j.dev/demo/openai/v1").build();// 最多保存10条会话ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);NamingMaster namingMaster = AiServices.builder(NamingMaster.class).chatLanguageModel(model).chatMemory(chatMemory).build();System.out.println(namingMaster.talk("帮我取一个很有中国文化内涵的女孩名字"));System.out.println("-----------------");System.out.println(namingMaster.talk("换一个"));}
}

控制台输出:

岚儿 (Lánér)

-----------------

芷若 (Zhǐruò)

由于使用了ChatMemory管理会话,大模型就理解了 “换一个” 的含义。

如果想指定最大的token数,如下:

// 设置最大token数为1000
ChatMemory chatMemory = TokenWindowChatMemory.withMaxTokens(1000, new OpenAiTokenizer("gpt-3.5-turbo"));

token的计算是与模型有关的,所以这里要指定当前使用的是哪个模型。

3.2 memoryId的使用

如果应用需要多个人使用,每个人都要有自己的会话,否则上下文就串了,可以使用memoryId来做区分。

package org.example;import dev.langchain4j.memory.chat.TokenWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiTokenizer;
import dev.langchain4j.service.*;public class Test3 {interface NamingMaster {String talk(@MemoryId String userId, @UserMessage String desc);}public static void main(String[] args) {ChatLanguageModel model = OpenAiChatModel.builder().apiKey("demo").baseUrl("http://langchain4j.dev/demo/openai/v1").build();NamingMaster namingMaster = AiServices.builder(NamingMaster.class).chatLanguageModel(model).chatMemoryProvider(uerId -> TokenWindowChatMemory.builder().id(uerId).maxTokens(1000, new OpenAiTokenizer("gpt-3.5-turbo")).build()).build();// 未设置memoryId 默认为 defaultSystem.out.println(namingMaster.talk("user1", "帮我取一个很有中国文化内涵的女孩名字"));System.out.println("-----------------");System.out.println(namingMaster.talk("user2", "换一个"));}}

控制台输出:

岚娜 (Lan Na)

-----------------

换一个什么?请问你需要什么样的帮助呢?

可以看到user2给大模型发消息: “换一个” ,大模型当做了一个新的会话了,这样就与user1隔离了。

4、大模型与外部系统交互

当大模型需要调用外部工具来获取特定信息或执行特定任务时,需要使用Tool,它提供了这些工具的必要信息,使得大模型能够理解工具,然后正确地调用它 。

使用场景

例如,在构建聊天机器人或智能助手时,大模型可能需要调用天气API来获取实时天气信息,或者调用数据库查询API来获取用户数据。在这些情况下,Tool可以帮助大模型准确地描述和调用这些外部工具。

package org.example;import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.*;import java.time.LocalDateTime;public class Test4 {interface Assistant {String chat(String userMessage);}static class MyTools {@Tool("获取当前日期")public static String dateUtil(String onUse) {return LocalDateTime.now().toString();}}public static void main(String[] args) {// 上面测试的key在此处不支持ChatLanguageModel model = OpenAiChatModel.builder().apiKey("your key").baseUrl("your base url").build();Assistant assistant = AiServices.builder(Assistant.class).chatLanguageModel(model).tools(new MyTools()).chatMemory(MessageWindowChatMemory.withMaxMessages(10)).build();String date = assistant.chat("获取今天的日期");System.out.println(date);}
}
gpt-3.5-turbo本身是无法获取今天的日期的

控制台输出:

今天:今天的日期是2024年7月11日。
2024-07-11 18:20:24 [main] dev.langchain4j.agent.tool.DefaultToolExecutor.execute()
DEBUG: About to execute ToolExecutionRequest { id = “call_BjJCXo78e6NeRrMrScp0A3Ip”, name = “dateUtil”, arguments = “{“arg0”:“after tomorrow”}” } for memoryId default
2024-07-11 18:20:24 [main] dev.langchain4j.agent.tool.DefaultToolExecutor.execute()
DEBUG: Tool execution result: 2024-07-11T18:20:24.931279900
后天:后天的日期是2024年7月13日。

注意 工具 相关的 demo key不支持,包括Streaming和生成图片同样也不支持,需要申请自己的key。

其他工具相关demo或者其他api请移步官方demo:langchain4j demo

5、其他国内大模型

国内的智谱 https://www.zhipuai.cn/

<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-zhipu-ai</artifactId><version>0.32.0</version>
</dependency>

国内的百度

<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-qianfan</artifactId><version>0.32.0</version>
</dependency>

查看官方其他支持大模型: Language Models | LangChain4j

6、写个应用

接下来,使用以上知识来做一个聊天助手:

6.1 配置spring boot相关包和配置文件

<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai-spring-boot-starter</artifactId><version>0.32.0</version>
</dependency>
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-spring-boot-starter</artifactId><version>0.32.0</version>
</dependency>

application.properties

langchain4j.open-ai.chat-model.api-key=填写你的key
langchain4j.open-ai.chat-model.base-url=填写你的URL
# langchain4j.open-ai.streaming-chat-model.api-key=server.port=9000
server.servlet.context-path=/ai

6.2 控制器ChatController

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.Map;@RestController
public class ChatController {ChatService chatService;@AutowiredChatController(ChatService chatService) {this.chatService = chatService;}@PostMapping("/reply")public String replyAssistant(@RequestBody Map<String, String> map) {String uid = map.get("uid");String msg = map.get("msg");System.out.println("userId:" + uid + " msg:" + msg);String result = chatService.speak(uid, msg);System.out.println(result);return result;}
}

6.3 接口ChatMaster

小应用体验好不好,除了大模型本身的能力外,关键在于提示词,通过优化提示词可以让大模型程序更智能。提示词我常用如下4种优化方式:

1、给大模型一个角色。比如你想让大模型生成孩子能理解的答案,可以告诉大模型它是一位幼儿园老师;

2、给大模型一个参考示例。如果你需要的格式是有要求的,比如每行需要有emoji表情包,那你可以把一个完成的示例发给大模型,让它参考输出;

3、让大模型一步一步解答。由于大模型是根据前面的提示词生成后面的提示词,对于逻辑比较复杂的问题时,往往出错,这个时候让它一步一步解答会有更好的结果;

4、告诉大模型它是提示词优化专家,让它帮你优化。

最后你要描述清楚你的问题,根据输出的结果不断调整提示词

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;public interface ChatMaster {// 以下提示词仅供参考@SystemMessage("你是一位恋爱大师,名叫贝贝。和你对话的是一位女孩,请根据上下文进行分析,然后以男生的角度进行回话。风格要幽默、有趣、体贴、温柔,适当扩展话题,让对话轻松愉快")String speak(@MemoryId String userId, @UserMessage String desc);
}

6.4 服务类ChatService

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class ChatService {private final ChatLanguageModel chatLanguageModel;@AutowiredChatService(ChatLanguageModel chatLanguageModel) {this.chatLanguageModel = chatLanguageModel;}private ChatMaster chatMaster;@PostConstructpublic void init() {chatMaster = AiServices.builder(ChatMaster.class).chatLanguageModel(chatLanguageModel)
//                .chatMemoryProvider(uerId -> MessageWindowChatMemory.builder().id(uerId).maxMessages(20).build()).chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(10)).build();}public String speak(String userId, String desc) {return chatMaster.speak(userId, desc);}
}

6.5 界面部分

前端vue3,使用uniapp开发,使用组件来显示头像,依赖了uni-ui,可以使用图片代替

<template><view class="ai-container"><view class="main"><view v-for="(message, index) in messages" :class="['record-item',index%2===1?'reverse':'']"><view class="portrait"><!-- <image v-if="index%2===0" src="../../static/img/icon-chatgpt.png"></image> --><uni-icons v-if="index%2===0" type="chat-filled" size="30" color="#fff"></uni-icons><uni-icons v-else type="person-filled" size="30" color="#fff"></uni-icons></view><view class="content" @longpress="handleLongPress(message)">{{message}}</view></view></view><view class="foot-box"><view class="text-box"><textarea v-model="userMessage" placeholder="请输入女生给你的回复" /></view><view class="send-btn-box"><button @click="sendMsg" class="uni-btn uni-btn-mini" type="primary" size="mini":loading="loading">发送</button></view></view></view>
</template><script setup>import {ref} from 'vue'const messages = ref(['我是一个帮你回复消息的AI,请把她回复给你的消息发给我,我帮你回复!长按可复制消息'])let uid = generateRandomString(8)let userMessage = ref('')let loading = ref(false)// 调用后台接口的函数  function sendMsg() {if(loading.value){return}let userMessageVal = userMessage.valueuserMessage.value = ''if (!userMessageVal) {uni.showToast({title: '请输入你的回复',icon: 'none'})return}loading.value = truemessages.value = [...messages.value, userMessageVal]uni.request({url: 'https://xxx.xxx.cn/ai/reply',method: 'POST',data: {uid: uid,msg: userMessageVal},success: (res) => {loading.value = falseconsole.log('res:', res)if (res.statusCode === 200) {const aiMessage = res.data// 将新数据添加到数组中  messages.value = [...messages.value, aiMessage]} else {console.error('数据加载失败', res);}},fail: (err) => {console.error('请求失败:', err);}});}function handleLongPress(textToCopy) {uni.setClipboardData({data: textToCopy,success: () => {uni.showToast({title: '复制成功',icon: 'success'});},fail: () => {uni.showToast({title: '复制失败',icon: 'none'});}});}function generateRandomString(length) {let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';let result = '';const charactersLength = characters.length;for (let i = 0; i < length; i++) {result += characters.charAt(Math.floor(Math.random() * charactersLength));}return result;}
</script><script>export default {data() {return {}},methods: {}}
</script><style lang="scss">.ai-container {display: flex;flex-direction: column;background-color: #fafafa;height: 100vh;/* #ifdef H5 */height: calc(100vh - 44px);/* #endif */.main {flex: 1;overflow-y: auto;padding: 10px;margin-top: 5px;.record-item {display: flex;margin-bottom: 15px;&.reverse {flex-direction: row-reverse;.content,.portrait {background-color: $theme-color-rgba;}}.portrait {display: flex;justify-content: center;align-items: center;width: 40px;min-width: 40px;height: 40px;background-color: #e8e8e8;image {width: 30px;height: 30px;}}.content {display: flex;align-items: center;margin: 0 10px;padding: 5px 10px;font-size: 14px;background-color: white;border-radius: 5px;}}}.foot-box {display: flex;background-color: white;height: 120px;.text-box {display: flex;flex: 1;textarea {margin: 10px 0px 10px 15px;padding: 10px;height: 80px;width: 100%;font-size: 14px;background-color: #fafafa;border-radius: 5px;}}.send-btn-box {display: flex;align-items: center;justify-content: center;.uni-btn {margin: 0 15px;padding: 0;width: 60px;}}}}
</style>

购买的key没用完,可以扫码微微体验一下,返回不了消息就是key用完了。请无视小程序本身的功能。

在这里插入图片描述

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

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

相关文章

光伏混合储能直流微网直流母线电压下垂控制MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介 此模型以混合储能系统为研究对象&#xff0c;采用基于关联参数SOC的改进下垂控制策略&#xff0c;将初始下垂系数与储能单元SOC的n次幂的比值作为现行下垂系数&#xff0c;通过改变n值&#xff0c;…

【快速上手ESP32(ESP-IDF)】ADC数模转换(含单次转换和连续转换以及校准)

这篇为重置版。 因为准备录制视频了&#xff0c;然后回过头看看之前讲ADC的文章发现有不少错误的地方&#xff08;但是代码是可以用的&#xff09;&#xff0c;而且讲的也不全面&#xff0c;因此决定写下这个重置版。 这边提供三种使用ADC的方法&#xff0c;第一种是老方法&a…

港科夜闻 | 香港科大与阿里巴巴合作,计划成立大数据与人工智能联合实验室

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大与阿里巴巴合作&#xff0c;计划成立大数据与人工智能联合实验室。香港科大7月19日与阿里巴巴集团签署合作备忘录&#xff0c;计划成立「香港科技大学–阿里巴巴大数据与人工智能联合实验室」&#xff0c;就生成…

科普文:万字梳理31个Kafka问题

1、 kafka 是什么,有什么作用 2、Kafka为什么这么快 3、Kafka架构及名词解释 4、Kafka中的AR、ISR、OSR代表什么 5、HW、LEO代表什么 6、ISR收缩性 7、kafka follower如何与leader同步数据 8、Zookeeper 在 Kafka 中的作用&#xff08;早期&#xff09; 9、Kafka如何快…

UDP程序设计

UDP协议概述 UDP&#xff0c;User Datagram Protocol&#xff0c;用户数据报协议&#xff0c;是一个简单的面向数据报(package-oriented)的传输层协议&#xff0c;规范为&#xff1a;RFC 768。 UDP提供数据的不可靠传递&#xff0c;它一旦把应用程序发给网络层的数据发送出去…

Java | Leetcode Java题解之第300题最长递增子序列

题目&#xff1a; 题解&#xff1a; class Solution {public int lengthOfLIS(int[] nums) {int len 1, n nums.length;if (n 0) {return 0;}int[] d new int[n 1];d[len] nums[0];for (int i 1; i < n; i) {if (nums[i] > d[len]) {d[len] nums[i];} else {int…

做一个能和你互动玩耍的智能机器人之四--固件

在openbot的firmware目录下我们能够找到arduino的固件源码和相关的文档。 openbot的controller目录下&#xff0c;是控制器的代码目录&#xff0c;用来控制机器人做一些动作。未来的目标是加入大模型&#xff0c;使其能够理解人的语言和动作来控制。 固件代码&#xff0c;支持…

利用小爱同学与点灯科技+esp8266+舵机,制作智能关灯神器:小白也可制作,米家同步设备可实现多部手机进行控制。(亲测有用)

利用小爱同学与点灯科技&#xff0c;制作智能关灯神器&#xff1a;小白也可制作&#xff0c;米家同步设备可实现多部手机进行控制。 文章目录 利用小爱同学与点灯科技&#xff0c;制作智能关灯神器&#xff1a;小白也可制作&#xff0c;米家同步设备可实现多部手机进行控制。1.…

19. Revit API: Parameter(参数)

一、前言 我们在前面或多或少提到也用到参数了&#xff0c;这篇便细讲一下。 首先&#xff0c;我们知道好多信息都藏在参数里&#xff0c;或者说可以从参数中获取。我们还能够通过调整参数的值&#xff0c;改变模型的形态&#xff0c;即族的参变。 其次&#xff0c;有时族上…

一文讲透如何使用CSS美化HTML界面

上一篇文章(初识HTML文件&#xff0c;创建自己的第一个网页&#xff01;)介绍了HTML文件&#xff0c;并实战练习了如何编写一个结构简单的HTML网页&#xff0c;但最后也提到了单纯使用HTML语言创建的网页并不美观&#xff0c;有没有一个东西像PS能够美化图片一样让我们能够对我…

【前端 16】使用Ajax发送异步请求

Ajax 基础入门&#xff1a;实现异步请求 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;是一种在无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术。通过使用 Ajax&#xff0c;可以在后台与服务器交换数据&#xff0c;这意味着可以在不影响用户…

【代码】Python3|Scrapy框架初探(汽车之家大连市二手车车辆数据爬取、清洗与可视化)

本篇主要是整个项目的介绍&#xff0c;没提到太多琐碎的技术细节&#xff0c;以后有空的话会整理一下 Scrapy 和原生爬虫的差异&#xff0c;还有它坑人的一些地方&#xff0c;单发出来。 开源地址&#xff1a;https://github.com/shandianchengzi/car_home_spider 使用说明&a…

Github 2024-07-28 php开源项目日报Top10

根据Github Trendings的统计,今日(2024-07-28统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Blade项目2TypeScript项目2Java项目1ASP项目1Laravel:表达力和优雅的 Web 应用程序框架 创建周期:4631 天开发语言:PHP, BladeStar…

openssl 加密

使用tar命令在Linux中加密文件可以通过两种方式实现&#xff1a;使用gzip压缩的同时加密&#xff0c;或者使用加密选项。 1. 使用gzip压缩的同时加密&#xff1a; “ tar cz file1 file2 | openssl enc -e -aes256 -out archive.tar.gz.enc “ – cz&#xff1a;创建tar压缩文…

基于粒子群优化算法(PSO)永磁同步电机电流环多参数辨识MATLAB仿真模型

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 仿真模型简介 在同步旋转dq 轴坐标系下建立PMSM 数学模型&#xff0c;将定子dq 轴电压设为辨识模型和实际测量值的输入&#xff0c;设计了PSO 辨识PMSM 参数的适应度函数。该辨识方法不需推导复杂的电机数学…

R语言统计分析——整合和重构

参考资料&#xff1a;R语言实战【第2版】 R中提供了许多用来整合&#xff08;aggregate&#xff09;和重塑&#xff08;reshape&#xff09;数据的强大方法。在整合数据时&#xff0c;往往将多组观测替换为根据这些观测计算的描述性统计量。在重塑数据时&#xff0c;则会通过修…

STM32——GPIO(点亮LEDLED闪烁)

一、什么是GPIO&#xff1f; GPIO&#xff08;通用输入输出接口&#xff09;&#xff1a; 1.GPIO 功能概述 GPIO 是通用输入/输出&#xff08;General Purpose I/O&#xff09;的简称&#xff0c;既能当输入口使用&#xff0c;又能当输出口使用。端口&#xff0c;就是元器件…

数据结构(5.3_5)——二叉树的线索化

第一种寻找中序前驱方法 中序线索化 本质上就是一次中序遍历&#xff0c;只不过需要在一边遍历一边处理结点线索化 代码&#xff1a; //全局变量pre 指向当前访问结点的前驱 ThreadNode* pre NULL;struct ElemType {int value; };//线索二叉树结点 typedef struct ThreadNode…

linux练习2

一、搭建nfs服务器&#xff0c;客户端可从服务端/share目录上传与下载文件 **服务端** 1、下载相关安装包 [rootserver ~]# yum install rpcbind -y [rootserver ~]# yum install nfs-utils -y 2、 创建共享文件夹/share并授予权限 [rootserver ~]# mkdir /share [rootserv…

vector以及迭代器失效

前言 学习完string&#xff0c;之后学习的就是vector。vector就是之前C语言中讲到过的顺序表&#xff0c;用三个变量分别记录资源的位置&#xff0c;容器的容量和容器中元素个数。原来的写法是直接使用指针加两个int变量&#xff0c;而标准库中&#xff0c;三个都是由指针确定的…