上下文驱动的 ECS:一种反应式实体组件系统扩展

引言

实体‑组件‑系统(Entity‑Component‑System,ECS)是一种以数据为中心的架构模式。它将标识(实体)、数据(组件)与行为(系统)解耦,通过组件组合定义对象,通过系统遍历具有特定组件集合的实体来实现逻辑。ECS 凭借其缓存友好、模块化强、易于扩展的特点,在游戏开发、实时仿真、高性能计算等领域得到了广泛应用。

然而,经典的 ECS 对全局状态(如时间、环境参数、应用模式)的处理往往比较笨拙。常见做法是使用"单件组件"(为全局数据单独创建一个实体)或"共享资源"(在系统间传递全局对象)。这些方式缺乏统一的生命周期管理,也难以实现响应式更新------当全局状态变化时,需要系统每帧轮询或手动触发更新。

本文介绍一种上下文驱动的 ECS 扩展(Context‑Driven ECS),它在基础 ECS 之上引入独立的上下文层,将全局数据封装为可观察、可嵌套的上下文对象,并通过反应式系统让实体行为能够自动响应上下文变化。这一设计既保留了 ECS 的高性能和模块性,又赋予系统对宏观状态的优雅处理能力。

  1. 基础 ECS 架构(ECS.Core)
    ECS.Core 实现了一个简洁但完备的 ECS 框架,包含以下核心要素:

实体(Entity):只读结构,包含 Id 和 Version,通过版本控制确保引用有效性。Entity.Invalid 表示无效实体。

组件(IComponent):标记接口,所有组件必须是结构体,以保证值类型语义和内存连续性。

组件数组(ComponentArray):内部使用稀疏数组(T[] 和 bool[])存储组件,实现 O(1) 的查找、添加和移除,且支持缓存友好的连续遍历。组件按类型独立存储,符合 SoA 布局。

实体管理器(EntityManager):负责实体的创建与销毁,组件的增删改查。它维护组件数组的字典,并提供基于组件组合的实体查询(GetEntities 系列方法),查询时自动选择组件数量最少的数组作为驱动以提高效率。

系统(ISystem):定义系统的生命周期(初始化、更新),通过 SystemManager 统一调度。系统可按需启用或禁用。

批量处理器(EntityBatchProcessor):提供对多个实体组件的批量更新操作,避免在循环中重复获取组件引用。

世界(EcsWorld):组合了 EntityManager 和 SystemManager,作为 ECS 的顶层容器。

该基础 ECS 遵循典型的数据驱动设计,组件按类型连续存储,适用于高性能遍历场景。

  1. 上下文管理模块(ContextManagement)
    ContextManagement 命名空间定义了一个独立于 ECS 的上下文容器,其设计融合了依赖注入与反应式编程的思想:

IContext 接口:支持父子层级(子上下文可继承父上下文的数据与服务)。每个上下文拥有唯一标识 Id、创建时间 CreatedAt 和一个关联的 CancellationToken,便于统一控制异步操作的生命周期。

类型化数据存储:通过 SetData、GetData 按类型(以及可选的字符串键)存储任意对象。数据查找会自动沿着父上下文链向上回溯,实现继承语义。

数据变更事件:DataChanged 事件在数据被添加、更新或移除时触发,传递包含新旧值和键名的事件参数。这使得上层可以订阅并响应数据变化。

服务定位:GetOrAddService 和 TryGetService 提供轻量级的服务解析。服务同样支持父子继承,便于共享基础设施(如日志、资源加载器)。

可观察数据:INotifyDataChanged 接口和 DirtyContextData 基类允许数据内部字段变更时发出通知。DirtyContextData 维护版本号和脏标记,并通过 SetField 方法实现字段级变更检测,自动触发 Changed 事件。

规则管理:RuleManager 允许注册 IContextRule 对象,并按优先级顺序执行规则。规则可用于维护上下文数据之间的一致性,或实现复杂的业务逻辑。

上下文模块完全独立,可单独用于任何需要全局状态管理的应用程序(如 MVC 中的 Model、前端状态管理)。

  1. ECS 上下文驱动扩展(ECS.ContextDriven)
    这一层将 ECS 与上下文管理无缝集成,使 ECS 系统能够感知并响应上下文数据的变化。

3.1 实体管理器与上下文的关联

通过 EntityManagerContextExtensions 静态类,每个 EntityManager 可以关联一个 IContext(使用 ConditionalWeakTable 实现弱引用关联)。这样,ECS 世界就拥有了一个共享的上下文环境。

3.2 上下文感知系统(ContextAwareSystem)

抽象类 ContextAwareSystem 继承自 SystemBase,在初始化时自动获取关联的上下文,并订阅其 DataChanged 事件。开发者只需实现 GetWatchedDataTypes 方法声明关心的数据类型,然后通过 OnContextDataChanged 虚方法或直接在 Update 中使用 TryGetContextData 获取最新数据。这大大简化了全局状态的使用。

3.3 上下文反应系统(ContextReactionSystem)

这是一个内置系统,它利用反射扫描程序集,自动注册所有实现了 IContextReactor 的类。当上下文数据变化时,反应系统按优先级顺序调用匹配的反应器(Reactor)。反应器可以基于新数据判断是否应执行操作(ShouldReact),并在 React 方法中操作实体管理器。

这种机制实现了数据驱动的反应式逻辑:无需每帧轮询,当上下文数据改变时,相关行为自动触发。例如,当 GameStateContextData 的当前阶段变为"暂停"时,一个反应器可以遍历所有敌方单位,冻结其 AI 组件。

3.4 预定义上下文数据类

为了开箱即用,扩展提供了几个典型的上下文数据类型,均继承自 DirtyContextData:

TimeContextData:管理时间缩放、暂停状态,自动计算每帧增量时间,并提供 Update 方法供驱动。

EnvironmentContextData:模拟天气、温度、湿度、风向等环境参数,字段变更时自动标记脏。

GameStateContextData:表示游戏阶段、得分、难度等级等宏观状态。

这些数据类内部使用 SetField 方法,在字段变化时自动触发 Changed 事件,进而引发上下文数据变更事件。

3.5 上下文驱动的世界(ContextDrivenWorld)

ContextDrivenWorld 是一个封装好的入口类,它同时管理 EntityManager、SystemManager 和一个默认上下文,并自动注册了 ContextReactionSystem。用户只需创建世界、定义组件和系统,然后每帧调用 Update,上下文数据便会随系统更新而自动传播。

  1. 数学分析应用场景
    上下文驱动的 ECS 架构在数学建模与数值计算领域同样展现出独特的价值。它将实体视为数学对象,组件存储状态变量和参数,系统实现数学算子,而上下文则提供全局约束、环境条件和控制参数。这种形式化为复杂数学问题的求解提供了一种模块化、可扩展的计算框架。

4.1 偏微分方程数值求解

在有限元、有限差分或谱方法中,计算域被离散为大量单元或网格点。

实体:每个网格节点或单元。

组件:Position(节点坐标)、Solution(场值,如温度)、StiffnessMatrixRow(稀疏矩阵非零元素,用于隐式求解)、BoundaryCondition(边界类型和值)。

系统:

组装系统:根据节点邻接关系组装全局矩阵(稀疏)。

时间推进系统:显式或隐式更新解(如 Runge‑Kutta 法)。

自适应网格细化系统:根据误差估计标记需要细化的单元。

上下文驱动:

TimeContextData:控制时间步长、模拟总时长,并支持自适应时间步。

EnvironmentContextData:存储材料参数(如热传导系数),这些参数可能随温度变化(非线性),通过上下文更新触发系统重新计算系数。

反应器:当边界条件变化时(例如热源强度改变),自动更新相关实体的边界组件,从而触发下一时间步的重新求解。

数学本质:将 PDE 离散化为大规模常微分方程组或代数系统,ECS 提供了一种面向对象的数据组织方式,使每个网格节点成为自治的计算单元,系统则实现数值通量计算和全局通信。上下文统一管理物理参数和时间控制,保证了模拟的物理一致性。

4.2 优化算法与机器学习

在参数优化、进化算法或神经网络训练中,需要维护大量候选解或网络参数。

实体:种群中的个体(粒子、染色体)或神经网络的层/神经元。

组件:Parameters(优化变量的当前值)、Fitness(适应度/损失值)、Velocity(粒子群算法)或 Momentum(梯度下降)。

系统:

评估系统:并行计算每个个体的适应度。

更新系统:根据优化策略更新参数(如粒子群的速度‑位置更新、遗传算法的交叉变异)。

选择系统:筛选优秀个体进入下一代。

上下文驱动:

GameStateContextData:表示当前优化阶段(探索/开发),影响变异概率或学习率。

EnvironmentContextData:提供目标函数的外部参数(如数据集分布),当数据变化时,自动触发重新评估所有个体的适应度。

反应器:当发现全局最优改进时,自动调整惯性权重或退火温度。

数学本质:优化过程通常描述为迭代格式

ECS 将每个迭代点视为实体,组件存储其状态,系统实现搜索方向和步长的计算。上下文提供超参数和终止条件,使得算法能够动态响应环境变化。

4.3 动态系统与混沌模拟

对于大量相互作用的动力系统(如天体运动、生态系统、流行病传播),每个个体遵循相同的动力学方程。

实体:每个质点、物种群体或感染者。

组件:State(系统状态向量,如位置、速度)、Parameters(个体参数,如质量、感染率)。

系统:

相互作用系统:计算所有实体对之间的作用力或感染概率(可用树形算法优化至 O(N log N))。

积分系统:使用数值积分器(如 Verlet、Runge‑Kutta)更新状态。

上下文驱动:

TimeContextData:提供时间步长,并支持事件触发(如碰撞检测后缩小步长)。

EnvironmentContextData:存储全局场(如引力场、气候数据),影响局部动力学。

反应器:当某实体越过边界时,自动施加反射或吸收条件,修改其速度组件。

数学本质:系统由常微分方程组

描述,ECS 将右端项计算分布到各实体,系统实现全局耦合。上下文管理时间自适应和外部驱动,保证长时间积分的稳定性。

4.4 数据同化与卡尔曼滤波

在地球物理或目标跟踪中,需要将观测数据实时融入模型,更新状态估计。

实体:网格点或状态向量分量。

组件:Background(背景场,先验估计)、Analysis(分析场,后验估计)、ErrorCovariance(误差协方差,对角或块对角近似)。

系统:

预报系统:将背景场向前传播(模型积分)。

更新系统:当新观测到达时,执行卡尔曼增益计算和状态更新。

上下文驱动:

观测数据作为上下文数据注入(如 ObservationContextData 包含观测值、观测算子、误差)。数据到达事件触发反应器,启动更新系统。

上下文还可以存储质量控制参数,当观测异常时调整权重。

数学本质:贝叶斯更新公式

ECS 将先验分布离散化为实体集合,系统实现预报和更新步骤,上下文携带观测信息,使同化过程模块化、可扩展。

4.5 随机过程与蒙特卡洛模拟

在金融定价、粒子输运或可靠性分析中,需要生成大量随机样本路径。

实体:每个样本路径或粒子。

组件:Position(当前状态,如资产价格)、Weight(重要性权重)、Status(是否存活,如粒子是否被吸收)。

系统:

传播系统:根据随机微分方程(SDE)推进每个样本。

分支/杀死系统:根据权重进行重采样。

统计系统:收集各时刻的期望值。

上下文驱动:

TimeContextData:控制时间离散化和终止时间。

EnvironmentContextData:存储模型参数(波动率、漂移率),可随市场状态变化(随机波动率模型),通过上下文更新触发传播系统的参数调整。

数学本质:蒙特卡洛方法的核心是生成独立同分布样本,ECS 天然支持大量实体并行处理。上下文管理全局参数和终止条件,使模拟能够适应变化的场景(如美式期权行权决策)。

  1. 创新性分析
    该扩展并非凭空创造,而是在 ECS 领域内对全局状态管理的一次系统性增强。其创新性体现在以下方面:

维度 现有 ECS 实践 本扩展的创新

全局数据 使用单件组件或资源,需手动管理生命周期 引入独立上下文,支持层级、继承、自动清理

数据变更通知 通常需每帧轮询比较 提供字段级脏标记和事件机制,实现反应式更新

系统对全局状态的依赖 系统内手动获取资源 上下文感知系统自动订阅,简化开发

逻辑触发方式 轮询或硬编码调用 反应系统按优先级自动调用反应器,解耦触发源与执行逻辑

上下文生命周期 缺乏统一管理 支持 IAsyncDisposable、取消令牌、父子关系

此外,将规则引擎(RuleManager)与 ECS 结合,为复杂业务场景提供了一致性维护手段。这些设计共同构成了一个反应式、上下文驱动的 ECS 范式,填补了 ECS 在全局状态反应式处理方面的空白。

当然,该扩展仍有优化空间,例如:

反应器可设计为异步或分帧执行,避免阻塞事件传播。

可扩展上下文数据的序列化支持,用于网络同步或保存。

支持基于键(Key)的细粒度订阅,减少不必要的反应调用。

  1. 结论
    本文介绍的上下文驱动 ECS 扩展,通过将独立、可观察的上下文层与经典 ECS 相结合,创造了一种新颖的编程模型。它不仅保留了 ECS 固有的高性能和模块化,还赋予了系统对宏观状态优雅的响应能力。该扩展已在多个数学与工程领域展现出应用潜力,其实用性和创新性值得关注。对于正在寻找高效管理全局状态方案的 ECS 开发者而言,这一模式无疑提供了有力的新工具。
csharp 复制代码
#region ECS.Core (基础ECS)
namespace ECS.Core
{
    public interface IComponent { }

    public readonly struct Entity : IEquatable<Entity>
    {
        public readonly int Id, Version;
        public Entity(int id, int version) => (Id, Version) = (id, version);
        public bool Equals(Entity other) => Id == other.Id && Version == other.Version;
        public override bool Equals(object obj) => obj is Entity other && Equals(other);
        public override int GetHashCode() => HashCode.Combine(Id, Version);
        public override string ToString() => $"Entity({Id}:{Version})";
        public static bool operator ==(Entity left, Entity right) => left.Equals(right);
        public static bool operator !=(Entity left, Entity right) => !left.Equals(right);
        public static readonly Entity Invalid = new(-1, -1);
        public bool IsValid => Id >= 0 && Version >= 0;
    }

    public struct Vec3
    {
        public static Vec3 right => new(1, 0, 0);
        public float X, Y, Z;
        public Vec3(float x, float y, float z) => (X, Y, Z) = (x, y, z);
        public Vec3 Normalized()
        {
            float length = (float)Math.Sqrt(X * X + Y * Y + Z * Z);
            return length > 0.0001f ? new Vec3(X / length, Y / length, Z / length) : new Vec3(0, 0, 0);
        }
        public static bool operator ==(Vec3 a, Vec3 b) =>
            !(float.IsNaN(a.X) || float.IsNaN(a.Y) || float.IsNaN(a.Z) ||
              float.IsNaN(b.X) || float.IsNaN(b.Y) || float.IsNaN(b.Z)) &&
            (float.IsInfinity(a.X) || float.IsInfinity(a.Y) || float.IsInfinity(a.Z) ||
             float.IsInfinity(b.X) || float.IsInfinity(b.Y) || float.IsInfinity(b.Z) ?
                a.X == b.X && a.Y == b.Y && a.Z == b.Z :
                Math.Abs(a.X - b.X) < 0.0001f && Math.Abs(a.Y - b.Y) < 0.0001f && Math.Abs(a.Z - b.Z) < 0.0001f);
        public static bool operator !=(Vec3 a, Vec3 b) => !(a == b);
        public override bool Equals(object obj) => obj is Vec3 vec && this == vec;
        public override int GetHashCode() => HashCode.Combine(X, Y, Z);
    }

    internal interface IComponentArray
    {
        bool HasComponent(int entityId);
        void RemoveComponent(int entityId);
        IEnumerable<int> GetAllEntities();
        int EntityCount { get; }
        Type ComponentType { get; }
        void Clear();
    }

    internal sealed class ComponentArray<T> : IComponentArray where T : struct, IComponent
    {
        private T[] _components = new T[256];
        private bool[] _hasComponent = new bool[256];
        private readonly HashSet<int> _entityIds = new();
        private int _count;

        public int EntityCount => _count;
        public Type ComponentType => typeof(T);

        public T GetComponent(int entityId)
        {
            if (entityId >= _components.Length || !_hasComponent[entityId])
                throw new InvalidOperationException($"Entity {entityId} does not have component {typeof(T).Name}");
            return _components[entityId];
        }

        public void SetComponent(int entityId, T component)
        {
            if (entityId >= _components.Length || !_hasComponent[entityId])
                throw new InvalidOperationException($"Entity {entityId} does not have component {typeof(T).Name}");
            _components[entityId] = component;
        }

        public void AddComponent(int entityId, T component)
        {
            EnsureCapacity(entityId);
            _components[entityId] = component;
            if (!_hasComponent[entityId])
            {
                _hasComponent[entityId] = true;
                _entityIds.Add(entityId);
                _count++;
            }
        }

        public bool TryGetComponent(int entityId, out T component)
        {
            if (entityId < _hasComponent.Length && _hasComponent[entityId])
            {
                component = _components[entityId];
                return true;
            }
            component = default;
            return false;
        }

        public void RemoveComponent(int entityId)
        {
            if (entityId < _hasComponent.Length && _hasComponent[entityId])
            {
                _hasComponent[entityId] = false;
                _entityIds.Remove(entityId);
                _count--;
            }
        }

        public bool HasComponent(int entityId) => entityId < _hasComponent.Length && _hasComponent[entityId];
        public IEnumerable<int> GetAllEntities() => _entityIds;
        public void Clear() { Array.Clear(_hasComponent, 0, _hasComponent.Length); _entityIds.Clear(); _count = 0; }

        private void EnsureCapacity(int entityId)
        {
            if (entityId >= _components.Length)
            {
                int newSize = Math.Max(_components.Length * 2, entityId + 1);
                Array.Resize(ref _components, newSize);
                Array.Resize(ref _hasComponent, newSize);
            }
        }
    }

    public sealed class EntityManager : IDisposable
    {
        private readonly Dictionary<Type, IComponentArray> _componentArrays = new();
        private readonly HashSet<int> _freeEntityIds = new();
        private readonly List<int> _entityVersions = new();
        private int _nextEntityId;
        private bool _disposed;

        public event Action<Entity>? EntityCreated, EntityDestroyed;
        public event Action<Entity, Type>? ComponentAdded, ComponentRemoved;

        public Entity CreateEntity()
        {
            ThrowIfDisposed();
            int id = _freeEntityIds.Count > 0 ? _freeEntityIds.Min() : _nextEntityId++;
            if (id != _nextEntityId - 1) _freeEntityIds.Remove(id);
            while (id >= _entityVersions.Count) _entityVersions.Add(0);
            var entity = new Entity(id, _entityVersions[id]);
            EntityCreated?.Invoke(entity);
            return entity;
        }

        public void DestroyEntity(Entity entity)
        {
            ThrowIfDisposed();
            if (!IsEntityValid(entity)) return;
            foreach (var array in _componentArrays.Values.Where(a => a.HasComponent(entity.Id)))
            {
                array.RemoveComponent(entity.Id);
                ComponentRemoved?.Invoke(entity, array.ComponentType);
            }
            EntityDestroyed?.Invoke(entity);
            _entityVersions[entity.Id]++;
            _freeEntityIds.Add(entity.Id);
        }

        public bool IsEntityValid(Entity entity) =>
            entity.Id >= 0 && entity.Id < _entityVersions.Count && entity.Version == _entityVersions[entity.Id];
        public int GetEntityCount() => _entityVersions.Count - _freeEntityIds.Count;

        public void AddComponent<T>(Entity entity, T component) where T : struct, IComponent
        {
            ThrowIfDisposed();
            if (!IsEntityValid(entity)) throw new ArgumentException($"Invalid entity: {entity}", nameof(entity));
            GetComponentArray<T>().AddComponent(entity.Id, component);
            ComponentAdded?.Invoke(entity, typeof(T));
        }

        public void RemoveComponent<T>(Entity entity) where T : struct, IComponent
        {
            ThrowIfDisposed();
            if (!IsEntityValid(entity)) return;
            var array = GetComponentArray<T>();
            if (array.HasComponent(entity.Id))
            {
                array.RemoveComponent(entity.Id);
                ComponentRemoved?.Invoke(entity, typeof(T));
            }
        }

        public T GetComponent<T>(Entity entity) where T : struct, IComponent
        {
            ThrowIfDisposed();
            if (!IsEntityValid(entity)) throw new ArgumentException($"Invalid entity: {entity}", nameof(entity));
            return GetComponentArray<T>().GetComponent(entity.Id);
        }

        public void SetComponent<T>(Entity entity, T component) where T : struct, IComponent
        {
            ThrowIfDisposed();
            if (!IsEntityValid(entity)) throw new ArgumentException($"Invalid entity: {entity}", nameof(entity));
            GetComponentArray<T>().SetComponent(entity.Id, component);
        }

        public bool TryGetComponent<T>(Entity entity, out T component) where T : struct, IComponent
        {
            ThrowIfDisposed();
            if (!IsEntityValid(entity)) { component = default; return false; }
            return GetComponentArray<T>().TryGetComponent(entity.Id, out component);
        }

        public bool HasComponent<T>(Entity entity) where T : struct, IComponent =>
            IsEntityValid(entity) && GetComponentArray<T>().HasComponent(entity.Id);

        public bool HasComponent(Entity entity, Type componentType) =>
            IsEntityValid(entity) && _componentArrays.TryGetValue(componentType, out var array) && array.HasComponent(entity.Id);

        public IEnumerable<Entity> GetEntities(params Type[] componentTypes)
        {
            ThrowIfDisposed();
            if (componentTypes.Length == 0) return Enumerable.Empty<Entity>();
            var startArray = FindSmallestComponentArray(componentTypes);
            if (startArray == null) return Enumerable.Empty<Entity>();
            return startArray.GetAllEntities()
                .Where(entityId => entityId >= 0 && entityId < _entityVersions.Count)
                .Select(entityId => new Entity(entityId, _entityVersions[entityId]))
                .Where(entity => IsEntityValid(entity) && componentTypes.All(type =>
                    _componentArrays.TryGetValue(type, out var array) && array.HasComponent(entity.Id)))
                .ToList();
        }

        public IEnumerable<Entity> GetEntities<T>() where T : struct, IComponent
        {
            ThrowIfDisposed();
            return _componentArrays.TryGetValue(typeof(T), out var array)
                ? array.GetAllEntities()
                    .Where(entityId => entityId >= 0 && entityId < _entityVersions.Count)
                    .Select(entityId => new Entity(entityId, _entityVersions[entityId]))
                    .Where(IsEntityValid).ToList()
                : Enumerable.Empty<Entity>();
        }

        public IEnumerable<Entity> GetEntities<T1, T2>() where T1 : struct, IComponent where T2 : struct, IComponent
        {
            ThrowIfDisposed();
            if (!_componentArrays.TryGetValue(typeof(T1), out var a1) || !_componentArrays.TryGetValue(typeof(T2), out var a2)) yield break;
            var (smaller, larger) = a1.EntityCount < a2.EntityCount ? (a1, a2) : (a2, a1);
            foreach (int id in smaller.GetAllEntities())
            {
                if (id >= 0 && id < _entityVersions.Count && larger.HasComponent(id))
                {
                    var entity = new Entity(id, _entityVersions[id]);
                    if (IsEntityValid(entity)) yield return entity;
                }
            }
        }

        public IEnumerable<Entity> GetAllEntities()
        {
            ThrowIfDisposed();
            return Enumerable.Range(0, _entityVersions.Count).Where(id => !_freeEntityIds.Contains(id))
                .Select(id => new Entity(id, _entityVersions[id])).ToList();
        }

        public void Clear()
        {
            ThrowIfDisposed();
            var all = GetAllEntities().ToList();
            foreach (var e in all)
            {
                foreach (var kv in _componentArrays.Where(kv => kv.Value.HasComponent(e.Id)))
                    ComponentRemoved?.Invoke(e, kv.Key);
                EntityDestroyed?.Invoke(e);
            }
            foreach (var arr in _componentArrays.Values) arr.Clear();
            _componentArrays.Clear();
            _freeEntityIds.Clear();
            _entityVersions.Clear();
            _nextEntityId = 0;
        }

        public void Dispose()
        {
            if (_disposed) return;
            Clear();
            _disposed = true;
            GC.SuppressFinalize(this);
        }

        private ComponentArray<T> GetComponentArray<T>() where T : struct, IComponent
        {
            var t = typeof(T);
            if (!_componentArrays.TryGetValue(t, out var arr))
                _componentArrays[t] = arr = new ComponentArray<T>();
            return (ComponentArray<T>)arr;
        }

        private IComponentArray? FindSmallestComponentArray(Type[] types) =>
            types.Where(t => _componentArrays.ContainsKey(t)).Select(t => _componentArrays[t])
                .OrderBy(arr => arr.EntityCount).FirstOrDefault();

        private void ThrowIfDisposed() { if (_disposed) throw new ObjectDisposedException(nameof(EntityManager)); }
    }

    public sealed class EntityQuery
    {
        private readonly EntityManager _em;
        private readonly List<Type> _required = new(), _excluded = new();
        public EntityQuery(EntityManager em) => _em = em ?? throw new ArgumentNullException(nameof(em));
        public EntityQuery WithComponent<T>() where T : struct, IComponent { _required.Add(typeof(T)); return this; }
        public EntityQuery WithoutComponent<T>() where T : struct, IComponent { _excluded.Add(typeof(T)); return this; }
        public IEnumerable<Entity> Execute() => _em.GetEntities(_required.ToArray())
            .Where(e => !_excluded.Any(t => _em.HasComponent(e, t)));
    }

    public static class EntityBatchProcessor
    {
        public delegate void ComponentAction<T>(Entity entity, ref T component) where T : struct, IComponent;
        public delegate void ComponentAction<T1, T2>(Entity entity, ref T1 c1, ref T2 c2) where T1 : struct, IComponent where T2 : struct, IComponent;
        public delegate void ComponentAction<T1, T2, T3>(Entity entity, ref T1 c1, ref T2 c2, ref T3 c3) where T1 : struct, IComponent where T2 : struct, IComponent where T3 : struct, IComponent;

        public static void Process<T>(EntityManager em, ComponentAction<T> a) where T : struct, IComponent
        {
            if (em == null) throw new ArgumentNullException(nameof(em));
            if (a == null) throw new ArgumentNullException(nameof(a));
            foreach (var e in em.GetEntities<T>().ToList())
            {
                var c = em.GetComponent<T>(e);
                a(e, ref c);
                em.SetComponent(e, c);
            }
        }

        public static void Process<T1, T2>(EntityManager em, ComponentAction<T1, T2> a) where T1 : struct, IComponent where T2 : struct, IComponent
        {
            if (em == null) throw new ArgumentNullException(nameof(em));
            if (a == null) throw new ArgumentNullException(nameof(a));
            foreach (var e in em.GetEntities<T1, T2>().ToList())
            {
                var c1 = em.GetComponent<T1>(e);
                var c2 = em.GetComponent<T2>(e);
                a(e, ref c1, ref c2);
                em.SetComponent(e, c1);
                em.SetComponent(e, c2);
            }
        }

        public static void Process<T1, T2, T3>(EntityManager em, ComponentAction<T1, T2, T3> a) where T1 : struct, IComponent where T2 : struct, IComponent where T3 : struct, IComponent
        {
            if (em == null) throw new ArgumentNullException(nameof(em));
            if (a == null) throw new ArgumentNullException(nameof(a));
            foreach (var e in em.GetEntities(typeof(T1), typeof(T2), typeof(T3)).ToList())
            {
                var c1 = em.GetComponent<T1>(e);
                var c2 = em.GetComponent<T2>(e);
                var c3 = em.GetComponent<T3>(e);
                a(e, ref c1, ref c2, ref c3);
                em.SetComponent(e, c1);
                em.SetComponent(e, c2);
                em.SetComponent(e, c3);
            }
        }
    }

    public interface ISystem
    {
        void Initialize(EntityManager entityManager);
        void Update(float deltaTime);
        bool Enabled { get; set; }
    }

    public abstract class SystemBase : ISystem, IDisposable
    {
        protected EntityManager EntityManager { get; private set; } = null!;
        public bool Enabled { get; set; } = true;
        public virtual void Initialize(EntityManager em) => EntityManager = em ?? throw new ArgumentNullException(nameof(em));
        public abstract void Update(float deltaTime);
        public virtual void Dispose() => OnDispose();
        protected virtual void OnDispose() { }
    }

    public sealed class SystemManager : IDisposable
    {
        private readonly EntityManager _em;
        private readonly List<ISystem> _systems = new();
        private readonly Dictionary<Type, ISystem> _systemByType = new();
        private bool _disposed;

        public SystemManager(EntityManager em) => _em = em ?? throw new ArgumentNullException(nameof(em));
        public T RegisterSystem<T>() where T : ISystem, new() => RegisterSystem(new T());
        public T RegisterSystem<T>(T system) where T : ISystem
        {
            ThrowIfDisposed();
            system.Initialize(_em);
            _systems.Add(system);
            _systemByType[typeof(T)] = system;
            return system;
        }
        public T GetSystem<T>() where T : ISystem => TryGetSystem<T>(out var s) ? s : throw new KeyNotFoundException($"System {typeof(T).Name} not found");
        public bool TryGetSystem<T>([NotNullWhen(true)] out T? s) where T : ISystem
        {
            if (_systemByType.TryGetValue(typeof(T), out var sys)) { s = (T)sys; return true; }
            s = default; return false;
        }
        public void UpdateAll(float dt) { ThrowIfDisposed(); foreach (var sys in _systems.Where(s => s.Enabled)) sys.Update(dt); }
        public void Dispose()
        {
            if (_disposed) return;
            foreach (var sys in _systems.OfType<IDisposable>()) sys.Dispose();
            _systems.Clear(); _systemByType.Clear(); _disposed = true; GC.SuppressFinalize(this);
        }
        private void ThrowIfDisposed() { if (_disposed) throw new ObjectDisposedException(nameof(SystemManager)); }
    }

    public sealed class EcsWorld : IDisposable
    {
        public EntityManager EntityManager { get; } = new();
        public SystemManager SystemManager { get; }
        private bool _disposed;
        public EcsWorld() => SystemManager = new SystemManager(EntityManager);
        public void Update(float dt) { ThrowIfDisposed(); SystemManager.UpdateAll(dt); }
        public EntityQuery CreateQuery() => new(EntityManager);
        public void Dispose()
        {
            if (_disposed) return;
            SystemManager.Dispose(); EntityManager.Dispose(); _disposed = true; GC.SuppressFinalize(this);
        }
        private void ThrowIfDisposed() { if (_disposed) throw new ObjectDisposedException(nameof(EcsWorld)); }
    }
}
#endregion

#region ContextManagement (统一上下文)
namespace ContextManagement
{
    public interface IContext : IDisposable, IAsyncDisposable
    {
        string Id { get; }
        IContext? Parent { get; }
        bool IsDisposed { get; }
        DateTime CreatedAt { get; }
        CancellationToken CancellationToken { get; }

        void SetData<T>(T value, string? key = null) where T : class;
        T GetData<T>(string? key = null) where T : class;
        bool TryGetData<T>([MaybeNullWhen(false)] out T? value, string? key = null) where T : class;
        bool RemoveData<T>(string? key = null) where T : class;
        bool ContainsData<T>(string? key = null) where T : class;
        IEnumerable<T> GetAllData<T>() where T : class;
        IEnumerable<object> GetAllData();

        event EventHandler<ContextDataChangedEventArgs>? DataChanged; // 统一事件

        T GetOrAddService<T>(Func<T> factory) where T : class;
        bool TryGetService<T>([MaybeNullWhen(false)] out T? service) where T : class;
    }

    public class ContextDataChangedEventArgs : EventArgs
    {
        public object? OldData { get; }
        public object NewData { get; }
        public string Key { get; }
        public ContextDataChangedEventArgs(object? oldData, object newData, string key)
        {
            OldData = oldData;
            NewData = newData;
            Key = key;
        }
    }

    public abstract class ContextBase : IContext
    {
        private readonly Dictionary<Type, Dictionary<string, object>> _typedData = new();
        private readonly ConcurrentDictionary<Type, object> _services = new();
        private readonly CancellationTokenSource? _cts;
        private bool _disposed;

        public string Id { get; }
        public IContext? Parent { get; }
        public bool IsDisposed => _disposed;
        public DateTime CreatedAt { get; }
        public CancellationToken CancellationToken => _cts?.Token ?? CancellationToken.None;

        public event EventHandler<ContextDataChangedEventArgs>? DataChanged;

        protected ContextBase(string? id = null, IContext? parent = null)
        {
            Id = id ?? Guid.NewGuid().ToString("N");
            Parent = parent;
            CreatedAt = DateTime.UtcNow;
            _cts = parent != null ? CancellationTokenSource.CreateLinkedTokenSource(parent.CancellationToken) : new CancellationTokenSource();
        }

        public void SetData<T>(T value, string? key = null) where T : class
        {
            if (value == null) throw new ArgumentNullException(nameof(value));
            key ??= string.Empty;
            var type = typeof(T);
            lock (_typedData)
            {
                if (!_typedData.TryGetValue(type, out var dict))
                {
                    dict = new Dictionary<string, object>();
                    _typedData[type] = dict;
                }
                object? old = null;
                if (dict.TryGetValue(key, out var existing))
                {
                    old = existing;
                    dict[key] = value;
                }
                else
                {
                    dict[key] = value;
                }

                // 处理数据内部变化通知
                if (value is INotifyDataChanged notify)
                    notify.Changed += OnDataItemChanged;
                if (old is INotifyDataChanged oldNotify)
                    oldNotify.Changed -= OnDataItemChanged;

                DataChanged?.Invoke(this, new ContextDataChangedEventArgs(old, value, key));
            }
        }

        public T GetData<T>(string? key = null) where T : class =>
            TryGetData<T>(out var v, key) ? v! : throw new KeyNotFoundException($"Data {typeof(T).Name} key '{key}' not found");

        public bool TryGetData<T>([MaybeNullWhen(false)] out T? value, string? key = null) where T : class
        {
            key ??= string.Empty;
            var type = typeof(T);
            lock (_typedData)
            {
                if (_typedData.TryGetValue(type, out var dict) && dict.TryGetValue(key, out var obj))
                {
                    value = (T)obj;
                    return true;
                }
            }
            if (Parent != null) return Parent.TryGetData<T>(out value, key);
            value = default;
            return false;
        }

        public bool RemoveData<T>(string? key = null) where T : class
        {
            key ??= string.Empty;
            var type = typeof(T);
            lock (_typedData)
            {
                if (_typedData.TryGetValue(type, out var dict) && dict.Remove(key, out var old))
                {
                    if (dict.Count == 0) _typedData.Remove(type);
                    if (old is INotifyDataChanged notify) notify.Changed -= OnDataItemChanged;
                    DataChanged?.Invoke(this, new ContextDataChangedEventArgs(old, null!, key)); // 移除时 NewData=null
                    return true;
                }
            }
            return false;
        }

        public bool ContainsData<T>(string? key = null) where T : class
        {
            key ??= string.Empty;
            var type = typeof(T);
            lock (_typedData)
            {
                if (_typedData.TryGetValue(type, out var dict) && dict.ContainsKey(key)) return true;
            }
            return Parent?.ContainsData<T>(key) ?? false;
        }

        public IEnumerable<T> GetAllData<T>() where T : class
        {
            lock (_typedData)
            {
                if (_typedData.TryGetValue(typeof(T), out var dict))
                    return dict.Values.Cast<T>().ToList();
            }
            return Enumerable.Empty<T>();
        }

        public IEnumerable<object> GetAllData()
        {
            lock (_typedData) { return _typedData.Values.SelectMany(d => d.Values).ToList(); }
        }

        private void OnDataItemChanged(object sender, EventArgs e)
        {
            // 数据内部变化,触发事件,无旧值
            DataChanged?.Invoke(this, new ContextDataChangedEventArgs(null, sender, ""));
        }

        public T GetOrAddService<T>(Func<T> factory) where T : class =>
            (T)_services.GetOrAdd(typeof(T), _ => factory?.Invoke() ?? throw new ArgumentNullException(nameof(factory)));

        public bool TryGetService<T>([MaybeNullWhen(false)] out T? service) where T : class
        {
            if (_services.TryGetValue(typeof(T), out var obj))
            {
                service = (T)obj;
                return true;
            }
            if (Parent != null) return Parent.TryGetService<T>(out service);
            service = default;
            return false;
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;
            if (disposing)
            {
                lock (_typedData)
                {
                    foreach (var dict in _typedData.Values)
                        foreach (var item in dict.Values)
                            if (item is INotifyDataChanged notify) notify.Changed -= OnDataItemChanged;
                    _typedData.Clear();
                }
                foreach (var s in _services.Values.OfType<IDisposable>()) try { s.Dispose(); } catch { }
                _services.Clear();
                _cts?.Cancel(); _cts?.Dispose();
            }
            _disposed = true;
        }

        protected virtual async ValueTask DisposeAsyncCore()
        {
            foreach (var s in _services.Values.OfType<IAsyncDisposable>())
                try { await s.DisposeAsync(); } catch { }
            foreach (var s in _services.Values.OfType<IDisposable>().Except(_services.Values.OfType<IAsyncDisposable>().OfType<IDisposable>()))
                try { s.Dispose(); } catch { }
            _services.Clear();
            lock (_typedData)
            {
                foreach (var dict in _typedData.Values)
                    foreach (var item in dict.Values)
                        if (item is INotifyDataChanged notify) notify.Changed -= OnDataItemChanged;
                _typedData.Clear();
            }
            _cts?.Cancel(); _cts?.Dispose();
        }

        public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
        public async ValueTask DisposeAsync() { await DisposeAsyncCore(); Dispose(false); GC.SuppressFinalize(this); }
        ~ContextBase() => Dispose(false);
    }

    public class ConcreteContext : ContextBase
    {
        public ConcreteContext(string? id = null, IContext? parent = null) : base(id, parent) { }
    }

    public interface INotifyDataChanged
    {
        event EventHandler? Changed;
    }

    public abstract class DirtyContextData : INotifyDataChanged
    {
        private long _version;
        private bool _isDirty;
        private readonly object _lock = new();
        public long Version => _version;
        public bool IsDirty => _isDirty;
        public event EventHandler? Changed;

        protected void SetField<T>(ref T field, T value)
        {
            if (EqualityComparer<T>.Default.Equals(field, value)) return;
            field = value;
            lock (_lock) { _version++; _isDirty = true; }
            Changed?.Invoke(this, EventArgs.Empty);
        }

        protected void MarkDirty()
        {
            lock (_lock) { _version++; _isDirty = true; }
            Changed?.Invoke(this, EventArgs.Empty);
        }

        public void ClearDirty() { lock (_lock) _isDirty = false; }
    }

    public interface IContextRule
    {
        int Priority { get; }
        void Execute(IContext rootContext);
    }

    public class RuleManager
    {
        private List<IContextRule> _rules = new();
        private readonly object _lock = new();
        public void RegisterRule(IContextRule rule)
        {
            lock (_lock) { _rules.Add(rule); _rules.Sort((a, b) => a.Priority.CompareTo(b.Priority)); }
        }
        public void ExecuteRules(IContext rootContext)
        {
            List<IContextRule> copy;
            lock (_lock) copy = _rules.ToList();
            foreach (var r in copy) r.Execute(rootContext);
        }
    }
}
#endregion

#region ECS.ContextDriven (ECS上下文驱动扩展)
namespace ECS.ContextDriven
{
    using ContextManagement;
    using ECS.Core;

    public static class EntityManagerContextExtensions
    {
        private static readonly ConditionalWeakTable<EntityManager, IContext> _contexts = new();
        public static IContext GetOrCreateContext(this EntityManager em) =>
            _contexts.GetValue(em, e => new ConcreteContext($"ECS-{Guid.NewGuid()}"));
        public static void SetContext(this EntityManager em, IContext ctx)
        {
            if (ctx == null) throw new ArgumentNullException(nameof(ctx));
            _contexts.Remove(em);
            _contexts.Add(em, ctx);
        }
        public static bool TryGetContext(this EntityManager em, out IContext? ctx) => _contexts.TryGetValue(em, out ctx);
    }

    public abstract class ContextAwareSystem : SystemBase
    {
        private IContext? _context;
        private readonly List<(Type Type, string Key)> _watched = new();

        public override void Initialize(EntityManager em)
        {
            base.Initialize(em);
            _context = em.GetOrCreateContext();
            foreach (var (t, k) in GetWatchedDataTypes())
                _watched.Add((t, k ?? string.Empty));
            _context.DataChanged += OnContextDataChanged;
        }

        public abstract IEnumerable<(Type Type, string? Key)> GetWatchedDataTypes();
        protected virtual void OnContextDataChanged(object? sender, ContextDataChangedEventArgs e) { }

        protected T GetContextData<T>(string? key = null) where T : class =>
            _context?.GetData<T>(key) ?? throw new InvalidOperationException("Context not initialized");
        protected bool TryGetContextData<T>(out T? data, string? key = null) where T : class
        {
            data = default;
            return _context != null && _context.TryGetData<T>(out data, key);
        }
        protected IEnumerable<T> GetAllContextData<T>() where T : class =>
            _context?.GetAllData<T>() ?? Enumerable.Empty<T>();

        protected override void OnDispose()
        {
            if (_context != null) _context.DataChanged -= OnContextDataChanged;
            base.OnDispose();
        }
    }

    public interface IContextReactor<T> where T : class
    {
        float Priority { get; }
        void React(T data, EntityManager entityManager);
        bool ShouldReact(T newData); // 基于新数据判断
    }

    internal class ContextReactorWrapper<T> : IContextReactor<object> where T : class
    {
        private readonly IContextReactor<T> _inner;
        public float Priority => _inner.Priority;
        public ContextReactorWrapper(IContextReactor<T> inner) => _inner = inner;
        public void React(object data, EntityManager em)
        {
            if (data is T t) _inner.React(t, em);
        }
        public bool ShouldReact(object newData) => newData is T t && _inner.ShouldReact(t);
    }

    public sealed class ContextReactionSystem : SystemBase
    {
        private readonly Dictionary<Type, List<IContextReactor<object>>> _reactors = new();
        private IContext? _context;
        private bool _scanned;
        private readonly object _scanLock = new();

        public override void Initialize(EntityManager em)
        {
            base.Initialize(em);
            _context = em.GetOrCreateContext();
            _context.DataChanged += OnContextDataChanged;
            EnsureScanned();
        }

        private void EnsureScanned()
        {
            if (_scanned) return;
            lock (_scanLock)
            {
                if (_scanned) return;
                ScanAndRegister();
                _scanned = true;
            }
        }

        private void ScanAndRegister()
        {
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                try
                {
                    var types = asm.GetTypes().Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType &&
                        t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IContextReactor<>)));
                    foreach (var rt in types)
                    {
                        try
                        {
                            var iface = rt.GetInterfaces().First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IContextReactor<>));
                            var dataType = iface.GetGenericArguments()[0];
                            if (!dataType.IsClass) continue;
                            var reactor = Activator.CreateInstance(rt);
                            if (reactor == null) continue;
                            GetType().GetMethod("RegisterReactor", BindingFlags.NonPublic | BindingFlags.Instance)
                                ?.MakeGenericMethod(dataType).Invoke(this, new[] { reactor });
                        }
                        catch { }
                    }
                }
                catch { }
            }
        }

        private void RegisterReactor<T>(IContextReactor<T> reactor) where T : class
        {
            var wrapper = new ContextReactorWrapper<T>(reactor);
            if (!_reactors.TryGetValue(typeof(T), out var list))
            {
                list = new List<IContextReactor<object>>();
                _reactors[typeof(T)] = list;
            }
            int idx = 0;
            while (idx < list.Count && list[idx].Priority > reactor.Priority) idx++;
            list.Insert(idx, wrapper);
        }

        private void OnContextDataChanged(object? sender, ContextDataChangedEventArgs e)
        {
            var dataType = e.NewData.GetType();
            if (_reactors.TryGetValue(dataType, out var reactors))
            {
                foreach (var r in reactors)
                {
                    try
                    {
                        if (r.ShouldReact(e.NewData))
                            r.React(e.NewData, EntityManager);
                    }
                    catch { }
                }
            }
        }

        public override void Update(float deltaTime) => EnsureScanned();
        protected override void OnDispose()
        {
            if (_context != null) _context.DataChanged -= OnContextDataChanged;
            base.OnDispose();
        }
    }

    // 预定义数据类
    public class TimeContextData : DirtyContextData
    {
        private float _deltaTime, _totalTime, _timeScale = 1f;
        private bool _paused;
        public float DeltaTime => _deltaTime;
        public float TotalTime => _totalTime;
        public float TimeScale => _timeScale;
        public bool IsPaused => _paused;
        public void Update(float dt)
        {
            if (!_paused)
            {
                _deltaTime = dt * _timeScale;
                _totalTime += _deltaTime;
                MarkDirty();
            }
            else if (_deltaTime != 0)
            {
                _deltaTime = 0;
                MarkDirty();
            }
        }
        public void SetTimeScale(float s) => SetField(ref _timeScale, Math.Max(s, 0f));
        public void SetPaused(bool p) => SetField(ref _paused, p);
    }

    public class EnvironmentContextData : DirtyContextData
    {
        public enum WeatherType { Clear, Rainy, Snowy, Foggy, Windy }
        private WeatherType _weather = WeatherType.Clear;
        private float _temperature = 20f, _humidity = 0.5f, _windSpeed;
        private Vec3 _windDir = Vec3.right;
        public WeatherType Weather => _weather;
        public float Temperature => _temperature;
        public float Humidity => _humidity;
        public float WindSpeed => _windSpeed;
        public Vec3 WindDirection => _windDir;
        public void SetWeather(WeatherType w) => SetField(ref _weather, w);
        public void SetTemperature(float t) => SetField(ref _temperature, t);
        public void SetHumidity(float h) => SetField(ref _humidity, Math.Clamp(h, 0, 1));
        public void SetWind(float speed, Vec3 dir)
        {
            var norm = dir.Normalized();
            if (Math.Abs(_windSpeed - speed) < 0.0001f && _windDir == norm) return;
            _windSpeed = speed;
            _windDir = norm;
            MarkDirty();
        }
    }

    public class GameStateContextData : DirtyContextData
    {
        public enum GamePhase { MainMenu, Playing, Paused, GameOver, Victory }
        private GamePhase _phase = GamePhase.MainMenu;
        private int _score, _level = 1;
        private float _difficulty = 1f;
        public GamePhase CurrentPhase => _phase;
        public int Score => _score;
        public int Level => _level;
        public float Difficulty => _difficulty;
        public void SetPhase(GamePhase p) => SetField(ref _phase, p);
        public void AddScore(int pts) { if (pts != 0) SetField(ref _score, _score + pts); }
        public void SetLevel(int l) => SetField(ref _level, Math.Max(1, l));
        public void SetDifficulty(float d) => SetField(ref _difficulty, Math.Max(d, 0.1f));
    }

    public sealed class ContextDrivenWorld : IDisposable
    {
        public EntityManager EntityManager { get; }
        public SystemManager SystemManager { get; }
        public IContext Context { get; }
        private bool _disposed;

        public ContextDrivenWorld()
        {
            EntityManager = new EntityManager();
            SystemManager = new SystemManager(EntityManager);
            Context = new ConcreteContext("ContextWorld");
            EntityManager.SetContext(Context);
            Context.SetData(new TimeContextData());
            Context.SetData(new GameStateContextData());
            Context.SetData(new EnvironmentContextData());
            SystemManager.RegisterSystem<ContextReactionSystem>();
        }

        public T RegisterContextData<T>(T data, string? key = null) where T : class { Context.SetData(data, key); return data; }
        public T GetContextData<T>(string? key = null) where T : class => Context.GetData<T>(key);
        public bool TryGetContextData<T>(out T? data, string? key = null) where T : class => Context.TryGetData<T>(out data, key);
        public EntityQuery CreateQuery() => new(EntityManager);

        public void Update(float deltaTime)
        {
            if (_disposed) throw new ObjectDisposedException(nameof(ContextDrivenWorld));
            if (Context.TryGetData<TimeContextData>(out var time)) time!.Update(deltaTime);
            SystemManager.UpdateAll(deltaTime);
        }

        public void Dispose()
        {
            if (_disposed) return;
            SystemManager.Dispose();
            EntityManager.Dispose();
            Context.Dispose();
            _disposed = true;
        }
    }
}
#endregion
相关推荐
fu的博客1 小时前
【数据结构6】栈的四种形态:递增/递减,满栈/空栈深度解析
数据结构
xiaoye-duck1 小时前
《算法题讲解指南:优选算法-双指针》--03快乐数,04盛水最多的容器
c++·算法
铸人1 小时前
再论自然数全加和 - 质数螺旋
数学·算法·数论·复数
汉克老师1 小时前
GESP2024年3月认证C++二级( 第一部分选择题(1-8))
c++·算法·循环结构·分支结构·gesp二级·gesp2级
坚持就完事了2 小时前
数据结构之堆(Java\Python双语实现)
java·数据结构·算法
自然语2 小时前
人工智能之数字生命-观察的实现
数据结构·人工智能·学习·算法
苦藤新鸡2 小时前
63.排序数组中找元素的第一个元素和最后一个元素
算法·leetcode
苦藤新鸡2 小时前
59 分割回文串
算法
得一录2 小时前
LoRA(Low-Rank Adaptation)的原理和实现
python·算法·机器学习