emacs 源码分析(七)
文章目录
- `emacs`源码分析(七)
- 自己动手把`emacs`的`DEFUN`宏抠出来
<2024-01-07 周日>
emacs源码分析(七)
这DEFUN宏就像胶水一样,它把c代码和emacs-lisp代码给联系起来。但是DEFUN宏看着怪恐怖的有没有!
/* This version of DEFUN declares a function prototype with the rightarguments, so we can catch errors with maxargs at compile-time. */
#define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc) \SUBR_SECTION_ATTRIBUTE \static union Aligned_Lisp_Subr sname = \{{{ PVEC_SUBR << PSEUDOVECTOR_AREA_BITS }, \{ .a ## maxargs = fnname }, \minargs, maxargs, lname, {intspec}, 0}}; \Lisp_Object fnname
自己动手把emacs的DEFUN宏抠出来
为了方便理解,我把DEFUN宏给抠了出来,放在一个单独的工程里:ysouyno/t_emacs_defun,如果不想下载工程,本篇结尾会附上所有源码(仅一个文件,不到300行代码)。
关于这个工程要注意:
- 仅适用于
windows平台,为了编译方便,很多辅助宏能省略则省略。 - 设置
C++ Language Standard为ISO C++20 Standard (/std:c++20)。
挑了一个最简单的emacs-lisp函数eq:
DEFUN ("eq", Feq, Seq, 2, 2, 0,doc: /* Return t if the two args are the same Lisp object. */attributes: const)(Lisp_Object obj1, Lisp_Object obj2)
{if (EQ (obj1, obj2))return Qt;return Qnil;
}
展开后的eq代码是:
static union Aligned_Lisp_Subr Seq =
{{{ PVEC_SUBR << PSEUDOVECTOR_AREA_BITS }, // struct Lisp_Subr::header{.a2 = Feq }, // struct Lisp_Subr::function2, // struct Lisp_Subr::min_args2, // struct Lisp_Subr::max_args"eq", // struct Lisp_Subr::symbol_name{0}, // struct Lisp_Subr::intspec0 // struct Lisp_Subr::doc}
};Lisp_Object Feq(Lisp_Object obj1, Lisp_Object obj2)
{if (EQ(obj1, obj2))return Qt;return Qnil;
}
从上可得,DEFUN有两个任务以(eq函数为例):
- 声明一个静态变量
Seq,它应该会将要用于emacs-lisp代码中的某些地方,目前我还不清楚细节。 - 定义一个
c函数Feq。
我照着DEFUN宏展开后样子定义了一个没有使用DEFUN宏来定义的函数my-eq,它可以正常工作:
// DEFUN("my-eq", ...)
static union Aligned_Lisp_Subr Smy_eq =
{ {{ PVEC_SUBR << PSEUDOVECTOR_AREA_BITS },{.a2 = Fmy_eq },2, 2, "my-eq", {0}, 0} };
Lisp_Object Fmy_eq(Lisp_Object obj1, Lisp_Object obj2)
{if (EQ(obj1, obj2))return Qt;return Qnil;
}
备注:
- 有一个
EXFUN宏要关注一下,这个代码我也抠出来了,它是用于声明Feq,否则编译器要报怨的:
error C2065: 'Feq': undeclared identifier
- 在
emacs源代码中,大量EXFUN的函数声明在globals.h文件中。该文件的生成方法见:“emacs源码分析(一)”。
附完整代码:
// t_emacs_defun.cpp : This file contains the 'main' function. Program execution begins and ends there.
//#include <stddef.h> // for ptrdiff_t
#include <stdio.h>typedef long long EMACS_INT;
typedef EMACS_INT Lisp_Word;#define SUBR_SECTION_ATTRIBUTE/* Minimum alignment requirement for Lisp objects, imposed by theinternal representation of tagged pointers. It is 2**GCTYPEBITS ifUSE_LSB_TAG, 1 otherwise. It must be a literal integer constant,for older versions of GCC (through at least 4.9). */
#if USE_LSB_TAG
# define GCALIGNMENT 8
# if GCALIGNMENT != 1 << GCTYPEBITS
# error "GCALIGNMENT and GCTYPEBITS are inconsistent"
# endif
#else
# define GCALIGNMENT 1
#endif#define GCALIGNED_UNION_MEMBER char alignas (GCALIGNMENT) gcaligned;#if HAVE_STRUCT_ATTRIBUTE_ALIGNED
# define GCALIGNED_STRUCT __attribute__ ((aligned (GCALIGNMENT)))
#else
# define GCALIGNED_STRUCT
#endifunion vectorlike_header
{/* The main member contains various pieces of information:- The MSB (ARRAY_MARK_FLAG) holds the gcmarkbit.- The next bit (PSEUDOVECTOR_FLAG) indicates whether this is a plainvector (0) or a pseudovector (1).- If PSEUDOVECTOR_FLAG is 0, the rest holds the size (numberof slots) of the vector.- If PSEUDOVECTOR_FLAG is 1, the rest is subdivided into three fields:- a) pseudovector subtype held in PVEC_TYPE_MASK field;- b) number of Lisp_Objects slots at the beginning of the objectheld in PSEUDOVECTOR_SIZE_MASK field. These objects are alwaystraced by the GC;- c) size of the rest fields held in PSEUDOVECTOR_REST_MASK andmeasured in word_size units. Rest fields may also includeLisp_Objects, but these objects usually needs some special treatmentduring GC.There are some exceptions. For PVEC_FREE, b) is always zero. ForPVEC_BOOL_VECTOR and PVEC_SUBR, both b) and c) are always zero.Current layout limits the pseudovectors to 63 PVEC_xxx subtypes,4095 Lisp_Objects in GC-ed area and 4095 word-sized other slots. */ptrdiff_t size;
};/* A Lisp_Object is a tagged pointer or integer. Ordinarily it is aLisp_Word. However, if CHECK_LISP_OBJECT_TYPE, it is a wrapperaround Lisp_Word, to help catch thinkos like 'Lisp_Object x = 0;'.LISP_INITIALLY (W) initializes a Lisp object with a tagged valuethat is a Lisp_Word W. It can be used in a static initializer. */#ifdef CHECK_LISP_OBJECT_TYPE
typedef struct Lisp_Object { Lisp_Word i; } Lisp_Object;
# define LISP_OBJECT_IS_STRUCT
# define LISP_INITIALLY(w) {w}
# undef CHECK_LISP_OBJECT_TYPE
enum CHECK_LISP_OBJECT_TYPE { CHECK_LISP_OBJECT_TYPE = true };
#else
typedef Lisp_Word Lisp_Object;
# define LISP_INITIALLY(w) (w)
enum CHECK_LISP_OBJECT_TYPE { CHECK_LISP_OBJECT_TYPE = false };
#endif/* This structure describes a built-in function.It is generated by the DEFUN macro only.defsubr makes it into a Lisp object. */struct Lisp_Subr
{union vectorlike_header header;union {Lisp_Object(*a0) (void);Lisp_Object(*a1) (Lisp_Object);Lisp_Object(*a2) (Lisp_Object, Lisp_Object);Lisp_Object(*a3) (Lisp_Object, Lisp_Object, Lisp_Object);Lisp_Object(*a4) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);Lisp_Object(*a5) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);Lisp_Object(*a6) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);Lisp_Object(*a7) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);Lisp_Object(*a8) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);Lisp_Object(*aUNEVALLED) (Lisp_Object args);Lisp_Object(*aMANY) (ptrdiff_t, Lisp_Object*);} function;short min_args, max_args;const char* symbol_name;union {const char* intspec;Lisp_Object native_intspec;};EMACS_INT doc;
#ifdef HAVE_NATIVE_COMPLisp_Object native_comp_u;char* native_c_name;Lisp_Object lambda_list;Lisp_Object type;
#endif
} GCALIGNED_STRUCT;union Aligned_Lisp_Subr
{struct Lisp_Subr s;GCALIGNED_UNION_MEMBER
};enum pvec_type
{PVEC_NORMAL_VECTOR, /* Should be first, for sxhash_obj. */PVEC_FREE,PVEC_BIGNUM,PVEC_MARKER,PVEC_OVERLAY,PVEC_FINALIZER,PVEC_MISC_PTR,PVEC_USER_PTR,PVEC_PROCESS,PVEC_FRAME,PVEC_WINDOW,PVEC_BOOL_VECTOR,PVEC_BUFFER,PVEC_HASH_TABLE,PVEC_TERMINAL,PVEC_WINDOW_CONFIGURATION,PVEC_SUBR,PVEC_OTHER, /* Should never be visible to Elisp code. */PVEC_XWIDGET,PVEC_XWIDGET_VIEW,PVEC_THREAD,PVEC_MUTEX,PVEC_CONDVAR,PVEC_MODULE_FUNCTION,PVEC_NATIVE_COMP_UNIT,/* These should be last, for internal_equal and sxhash_obj. */PVEC_COMPILED,PVEC_CHAR_TABLE,PVEC_SUB_CHAR_TABLE,PVEC_RECORD,PVEC_FONT /* Should be last because it's used for range checking. */
};enum More_Lisp_Bits
{/* For convenience, we also store the number of elements in these bits.Note that this size is not necessarily the memory-footprint size, butonly the number of Lisp_Object fields (that need to be traced by GC).The distinction is used, e.g., by Lisp_Process, which places extranon-Lisp_Object fields at the end of the structure. */PSEUDOVECTOR_SIZE_BITS = 12,PSEUDOVECTOR_SIZE_MASK = (1 << PSEUDOVECTOR_SIZE_BITS) - 1,/* To calculate the memory footprint of the pseudovector, it's usefulto store the size of non-Lisp area in word_size units here. */PSEUDOVECTOR_REST_BITS = 12,PSEUDOVECTOR_REST_MASK = (((1 << PSEUDOVECTOR_REST_BITS) - 1)<< PSEUDOVECTOR_SIZE_BITS),/* Used to extract pseudovector subtype information. */PSEUDOVECTOR_AREA_BITS = PSEUDOVECTOR_SIZE_BITS + PSEUDOVECTOR_REST_BITS,PVEC_TYPE_MASK = 0x3f << PSEUDOVECTOR_AREA_BITS
};/* This version of DEFUN declares a function prototype with the rightarguments, so we can catch errors with maxargs at compile-time. */
#define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc) \SUBR_SECTION_ATTRIBUTE \static union Aligned_Lisp_Subr sname = \{{{ PVEC_SUBR << PSEUDOVECTOR_AREA_BITS }, \{ .a ## maxargs = fnname }, \minargs, maxargs, lname, {intspec}, 0}}; \Lisp_Object fnnameenum maxargs
{MANY = -2,UNEVALLED = -1
};#define EXFUN(fnname, maxargs) \extern Lisp_Object fnname DEFUN_ARGS_ ## maxargs/* Note that the weird token-substitution semantics of ANSI C makesthis work for MANY and UNEVALLED. */
#define DEFUN_ARGS_MANY (ptrdiff_t, Lisp_Object *)
#define DEFUN_ARGS_UNEVALLED (Lisp_Object)
#define DEFUN_ARGS_0 (void)
#define DEFUN_ARGS_1 (Lisp_Object)
#define DEFUN_ARGS_2 (Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_3 (Lisp_Object, Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_4 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_5 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \Lisp_Object)
#define DEFUN_ARGS_6 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_7 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \Lisp_Object, Lisp_Object, Lisp_Object)
#define DEFUN_ARGS_8 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object)EXFUN(Feq, 2); // for error C2065: 'Feq': undeclared identifier
EXFUN(Fmy_eq, 2);#define lisp_h_XLI(o) (o)
#define XLI(o) lisp_h_XLI (o)
#define lisp_h_EQ(x, y) (XLI (x) == XLI (y))
#define EQ(x, y) lisp_h_EQ (x, y)#define Qt (Lisp_Object)1
#define Qnil (Lisp_Object)0/*
static union Aligned_Lisp_Subr Seq =
{{{ PVEC_SUBR << PSEUDOVECTOR_AREA_BITS }, // struct Lisp_Subr::header{.a2 = Feq }, // struct Lisp_Subr::function2, // struct Lisp_Subr::min_args2, // struct Lisp_Subr::max_args"eq", // struct Lisp_Subr::symbol_name{0}, // struct Lisp_Subr::intspec0 // struct Lisp_Subr::doc}
};Lisp_Object Feq(Lisp_Object obj1, Lisp_Object obj2)
{if (EQ(obj1, obj2))return Qt;return Qnil;
}
*/// error C7555: use of designated initializers requires at least '/std:c++20'
DEFUN("eq", Feq, Seq, 2, 2, 0,doc: /* Return t if the two args are the same Lisp object. */
attributes: const)
(Lisp_Object obj1, Lisp_Object obj2)
{if (EQ(obj1, obj2))return Qt;return Qnil;
}// DEFUN("my-eq", ...)
static union Aligned_Lisp_Subr Smy_eq =
{ {{ PVEC_SUBR << PSEUDOVECTOR_AREA_BITS },{.a2 = Fmy_eq },2, 2, "my-eq", {0}, 0} };
Lisp_Object Fmy_eq(Lisp_Object obj1, Lisp_Object obj2)
{if (EQ(obj1, obj2))return Qt;return Qnil;
}int main() {printf("Feq(0, 0): %s\n", Feq(0, 0) ? "true" : "false");printf("Feq(0, 1): %s\n", Feq(0, 1) ? "true" : "false");printf("Fmy_eq(11, 11): %s\n", Fmy_eq(0, 0) ? "true" : "false");printf("Fmy_eq(10, 11): %s\n", Fmy_eq(0, 1) ? "true" : "false");
}
程序输出:
Feq(0, 0): true
Feq(0, 1): false
Fmy_eq(11, 11): true
Fmy_eq(10, 11): false
相关文章:
emacs 源码分析(七)
文章目录 emacs源码分析(七)自己动手把emacs的DEFUN宏抠出来 <2024-01-07 周日> emacs源码分析(七) 这DEFUN宏就像胶水一样,它把c代码和emacs-lisp代码给联系起来。但是DEFUN宏看着怪恐怖的有没有!…...
Linux运维-Web服务器的配置与管理(Apache+tomcat)(没成功,最后有失败经验)
Web服务器的配置与管理(Apachetomcat) 项目场景 公司业务经过长期发展,有了很大突破,已经实现盈利,现公司要求加强技术架构应用功能和安全性以及开始向企业应用、移动APP等领域延伸,此时原来开发web服务的php语言已经不适应新的…...
探讨分布式数据库ID生成解决方案
在技术面试中,面试官通常通过挑战应聘者的分布式系统知识来评估其能力。今天,让我们模拟一场面试现场,深入了解关于分布式数据库ID生成的解决方案。 面试官:嘿,小伙子,分布式数据库ID生成解决方案了解吗&a…...
Clickhouse填坑记4:Too many parts问题分析
Clickhouse在进行大数据量同步时,感觉很爽,插入速度非常快,但是,在使用过程中却出现了几次“Too many parts”异常报错,搞得很痛苦,这里记录一下解决过程。 我这边采用的是Flink程序,实时将数据写入ClickHouse,在执行一段时间后,会提示“Too many parts”异常,如下异…...
CertiK CSO Dr. Kang Li 确认出席Hack .Summit() 香港区块链盛会
CertiK CSO Dr. Kang Li 确认将出席由 Hack VC 主办,并由 AltLayer 和 Berachain 联合主办,与 SNZ 和数码港合作,由 Techub News 承办的Hack.Summit() 2024区块链开发者盛会。 Dr. Kang Li 目前担任CertiK首席安全官。他是清华蓝莲花战队启蒙…...
C++ 游戏飞机大战, 字符型的
//#define _CRT_SECURE_NO_WARNINGS 1 用于禁止不安全函数的警告 #include<iostream> #include<stdlib.h> #include<string> #include<conio.h> #include<Windows.h> #include<time.h> #include <graphics.h> using namespace std;…...
用html编写的简易新闻页面
用html编写的简易新闻页面 相关代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document<…...
docker-mysql:5.7安装
1、下载mysql:5.7镜像 [rootlocalhost ~]# docker search mysql (某个XXX镜像名字) [rootlocalhost ~]# docker pull mysql:5.7 按装之前查看一下是否按装过mysql。如果安装过会占用3306端口。 [rootlocalhost ~]# ps -ef | grep mysql 2、简单的安装 [rootlocalhost ~]# d…...
SQLPro Studio:数据库管理的革命性工具 mac版
SQLPro Studio是一款强大的数据库管理和开发工具,它旨在提供高效、便捷和安全的数据库操作体验。无论是数据库管理员、开发人员还是数据分析师,SQLPro Studio都能满足他们在数据库管理、查询、设计和维护方面的需求。 SQLPro Studio mac版软件获取 首先…...
【小沐学QT】QT学习之OpenGL开发笔记
文章目录 1、简介2、Qt QOpenGLWidget gl函数3、Qt QOpenGLWidget qt函数4、Qt QOpenGLWindow5、Qt glut6、Qt glfw结语 1、简介 Qt提供了与OpenGL实现集成的支持,使开发人员有机会在更传统的用户界面的同时显示硬件加速的3D图形。 Qt有两种主要的UI开发方…...
kali安装ARL灯塔(docker)
1、root身份进入容器 ┌──(root㉿Kali)-[~/桌面] └─# su root ┌──(root㉿Kali)-[~/桌面] └─# docker 2、先更新再克隆 ┌──(root㉿Kali)-[~/桌面] └─# apt-get update …...
字节面试分享,请详细介绍为何Http Post发送两次请求
在浏览器中,内容是很开放的,任何资源都可以接入其中,如 JavaScript 文件、图片、音频、视频等资源,甚至可以下载其他站点的可执行文件。 但也不是说浏览器就是完全自由的,如果不加以控制,就会出现一些不可…...
Flink CDC 3.0 Starrocks建表失败会导致任务卡主!
Flink CDC 3.0 Starrocks建表失败会导致任务卡主! 现象 StarRocks建表失败,然后任务自动重启,重启完毕后数据回放,jobMaster打印下面日志后,整个任务会卡主 There are already processing requests. Wait for proce…...
基于 LVGL 使用 SquareLine Studio 快速设计 UI 界面
目录 简介注册与软件获取工程配置设计 UI导出源码板级验证更多内容 简介 SquareLine Studio 是一款专业的 UI 设计软件,它与 LVGL(Light and Versatile Graphics Library,轻量级通用图形库)紧密集成。LVGL 是一个轻量化的、开源的…...
Selenium IDE插件录制网页,解放双手
1、 国内下载地址 https://www.crx4chrome.com/crx/77585/ ,这个网络正常基本可以下载,目前最新版本是3.17.2。 点击Crx4Chrome下载。下载后的文件名称是:mooikfkahbdckldjjndioackbalphokd-3.17.2-Crx4Chrome.com.crx。 2、 安装 直接打开…...
【LeetCode】【滑动窗口长度不固定】978 最长湍流子数组
1794.【软件认证】最长的指定瑕疵度的元音子串 这个例题,是滑动窗口中长度不定求最大的题目,在看题之前可以先看一下【leetcode每日一题】【滑动窗口长度不固定】案例。 题目描述 定义:开头和结尾都是元音字母(aeiouAEIOU&…...
水库安全监测方案(福建地区水库安全监测案例分享)
我司星创易联最近在福建省受到了一个水库安全监测系统项目的委托。该水库位于福建中部山区,作为该地区的重要防洪与供水工程,对下游数十万人的生活产生重大影响。但是因为水库附近地质情况复杂,水库大坝在多次洪水冲击下出现一定病害,亟须全面加强对水库大坝安全状况的监测,以确…...
Oracle内存计算应用模式
前言 内存计算是利用内存来加速数据访问和应用的性能,并降低应用开发复杂度的技术。近十年来,随着软硬件技术的发展和用户需求的成熟,内存计算技术已经得到了广泛地应用。 Oracle在内存计算领域具有非常重要的地位,这主要得益于…...
ELK日志系统
一、规划 服务名所在服务器kafka1—2.13-2.4.1192.168.76.10kafka2—2.13-2.4.1192.168.76.11kafka3—2.13-2.4.1192.168.76.12zookeeper1—3.6.3192.168.76.10zookeeper2—3.6.3192.168.76.11zookeeper3—3.6.3192.168.76.12elasticsearch1—7.12.1192.168.76.10elasticsearc…...
C++:list容器(非原生指针迭代器的实现)
本章是STL容器 list 的模拟实现。 之前已经使用 C语言 对带头双向循环链表 进行实现,详见数据结构: 线性表(带头双向循环链表实现), 相较于之前的实现,C 下多了对迭代器以及模板等相关语法特性。下面将着重讲解这些新知识。 文章目录 一. list 的基本框架…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
