掌控 Solidity:事件日志、继承和接口的深度解析

Solidity 是以太坊智能合约的主要编程语言,它的强大之处在于能够帮助开发者构建安全、高效的去中心化应用。在我参与的多个项目中,事件日志、继承和接口这三个概念始终贯穿其中,成为构建复杂智能合约的关键技术。今天就来聊聊Solidity中的错误处理、事件日志、继承和接口。

Solidity中的错误处理

在 Solidity 中,错误处理是非常重要的,它可以帮助开发者捕获和处理合约执行过程中可能出现的问题,从而提高合约的健壮性和安全性。Solidity 提供了多种机制来处理错误,包括 require、assert、revert 和自定义错误。

require 语句

require 语句用于在条件不满足时抛出异常并回滚交易。通常用于验证输入参数和外部状态。

语法

require(condition, "Error message");

示例

function donate(uint256 projectId) public payable {
  require(projectId <= projectCount, "Invalid project ID");
  require(block.timestamp <= projects[projectId].deadline, "Project deadline has passed");
  require(msg.value > 0, "Donation amount must be greater than 0");

  // 其他逻辑
}
assert 语句

assert 语句用于在条件不满足时抛出异常并回滚交易。通常用于检测内部错误,例如不变量检查。

语法

assert(condition);

示例

function withdrawFunds(uint256 projectId) public {
  require(projectId <= projectCount, "Invalid project ID");
  require(projects[projectId].creator == msg.sender, "Only the project creator can withdraw funds");
  require(projects[projectId].isFunded, "Project is not funded");

  uint256 amountToWithdraw = projects[projectId].raisedAmount;
  projects[projectId].raisedAmount = 0;

  (bool success, ) = projects[projectId].creator.call{value: amountToWithdraw}("");
  assert(success); // 确保转账成功

  emit Funded(projectId, amountToWithdraw);
}
revert 语句

revert 语句用于显式地抛出异常并回滚交易。可以传递一个字符串作为错误消息。

语法

revert("Error message");

示例:

function createProject(string memory title, string memory description, uint256 targetAmount, uint256 duration) public {
  if (targetAmount == 0) {
    revert("Target amount must be greater than 0");
  }
  if (duration == 0) {
    revert("Duration must be greater than 0");
  }

  projectCount++;
  uint256 deadline = block.timestamp + duration;
  projects[projectCount] = Project(msg.sender, title, description, targetAmount, 0, deadline, false);
  emit ProjectCreated(projectCount, msg.sender, title, targetAmount, deadline);
}
自定义错误

从 Solidity 0.8.0 版本开始,引入了自定义错误功能,可以提高错误处理的可读性和效率。

定义自定义错误

error InvalidProjectId();
error DeadlinePassed();
error ZeroDonation();
error NotProjectCreator();
error NotFunded();

抛出自定义错误

function donate(uint256 projectId) public payable {
  if (projectId > projectCount) {
    revert InvalidProjectId();
  }
  if (block.timestamp > projects[projectId].deadline) {
    revert DeadlinePassed();
  }
  if (msg.value == 0) {
    revert ZeroDonation();
  }

  projects[projectId].raisedAmount += msg.value;
  emit Donated(projectId, msg.sender, msg.value);

  if (projects[projectId].raisedAmount >= projects[projectId].targetAmount) {
    projects[projectId].isFunded = true;
    emit Funded(projectId, projects[projectId].raisedAmount);
  }
}
错误处理的最佳实践

明确错误消息
使用清晰、具体的错误消息,帮助调试和理解问题。

避免冗余检查
不要在多个地方重复相同的检查,尽量集中处理。

使用自定义错误
自定义错误可以提高代码的可读性和可维护性,减少 gas 费用。

合理使用 assert 和 require
assert 用于检测内部错误,require 用于验证外部输入和状态。

测试错误处理
编写单元测试来验证错误处理逻辑是否正确。

示例合约

以下是一个完整的示例合约,展示了如何使用 requireassertrevert 和自定义错误:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract CrowdfundingPlatform is Ownable, ReentrancyGuard {
    struct Project {
        address creator;
        string title;
        string description;
        uint256 targetAmount;
        uint256 raisedAmount;
        uint256 deadline;
        bool isFunded;
    }

    mapping(uint256 => Project) public projects;
    uint256 public projectCount;

    event ProjectCreated(uint256 projectId, address creator, string title, uint256 targetAmount, uint256 deadline);
    event Donated(uint256 projectId, address donor, uint256 amount);
    event Funded(uint256 projectId, uint256 totalRaised);

    error InvalidProjectId();
    error DeadlinePassed();
    error ZeroDonation();
    error NotProjectCreator();
    error NotFunded();

    function createProject(string memory title, string memory description, uint256 targetAmount, uint256 duration) public {
        if (targetAmount == 0) {
            revert("Target amount must be greater than 0");
        }
        if (duration == 0) {
            revert("Duration must be greater than 0");
        }

        projectCount++;
        uint256 deadline = block.timestamp + duration;
        projects[projectCount] = Project(msg.sender, title, description, targetAmount, 0, deadline, false);
        emit ProjectCreated(projectCount, msg.sender, title, targetAmount, deadline);
    }

    function donate(uint256 projectId) public payable {
        if (projectId > projectCount) {
            revert InvalidProjectId();
        }
        if (block.timestamp > projects[projectId].deadline) {
            revert DeadlinePassed();
        }
        if (msg.value == 0) {
            revert ZeroDonation();
        }

        projects[projectId].raisedAmount += msg.value;
        emit Donated(projectId, msg.sender, msg.value);

        if (projects[projectId].raisedAmount >= projects[projectId].targetAmount) {
            projects[projectId].isFunded = true;
            emit Funded(projectId, projects[projectId].raisedAmount);
        }
    }

    function withdrawFunds(uint256 projectId) public nonReentrant {
        if (projectId > projectCount) {
            revert InvalidProjectId();
        }
        if (projects[projectId].creator != msg.sender) {
            revert NotProjectCreator();
        }
        if (!projects[projectId].isFunded) {
            revert NotFunded();
        }

        uint256 amountToWithdraw = projects[projectId].raisedAmount;
        projects[projectId].raisedAmount = 0;

        (bool success, ) = projects[projectId].creator.call{value: amountToWithdraw}("");
        assert(success); // 确保转账成功

        emit Funded(projectId, amountToWithdraw);
    }
}

Solidity中的事件和日志

什么是事件?

在 Solidity 中,事件是一种允许智能合约与外部世界进行通信的机制。通过触发事件,可以记录合约执行中的关键操作,并将这些操作发送到链上。事件的记录会以日志的形式存储在区块中,不会直接改变合约的状态。

为什么使用事件?
  • 成本低:事件数据存储在日志中,比存储在合约状态中更便宜。
  • 可检索:事件数据可以被链外应用轻松检索和解析。
  • 异步通知:事件可以用于异步通知链外应用,实现实时更新。
定义和触发事件

定义事件
在 Solidity 中,事件的定义使用 event 关键字。事件可以带有参数,这些参数可以在触发事件时传递值。

event ProjectCreated(uint256 indexed projectId, address indexed creator, string title, uint256 targetAmount, uint256 deadline);
event Donated(uint256 indexed projectId, address indexed donor, uint256 amount);
event Funded(uint256 indexed projectId, uint256 totalRaised);
  • indexed 关键字:标记参数为索引参数,可以在日志中快速查找。最多可以有三个索引参数。

触发事件
在合约方法中,使用 emit 关键字来触发事件。

function createProject(string memory title, string memory description, uint256 targetAmount, uint256 duration) public {
    projectCount++;
    uint256 deadline = block.timestamp + duration;
    projects[projectCount] = Project(msg.sender, title, description, targetAmount, 0, deadline, false);
    emit ProjectCreated(projectCount, msg.sender, title, targetAmount, deadline);
}

function donate(uint256 projectId) public payable {
    require(projectId <= projectCount, "Invalid project ID");
    require(block.timestamp <= projects[projectId].deadline, "Project deadline has passed");
    require(msg.value > 0, "Donation amount must be greater than 0");

    projects[projectId].raisedAmount += msg.value;
    emit Donated(projectId, msg.sender, msg.value);

    if (projects[projectId].raisedAmount >= projects[projectId].targetAmount) {
        projects[projectId].isFunded = true;
        emit Funded(projectId, projects[projectId].raisedAmount);
    }
}
监听和检索事件

监听事件
在链外应用中,可以使用 Web3.js 或其他以太坊客户端库来监听事件。

const projectCreatedEvent = crowdfundingPlatform.events.ProjectCreated();
projectCreatedEvent.on('data', (event) => {
    console.log(`Project created: ${event.returnValues.projectId}`);
});

const donatedEvent = crowdfundingPlatform.events.Donated();
donatedEvent.on('data', (event) => {
    console.log(`Donated to project ${event.returnValues.projectId}: ${event.returnValues.amount} wei`);
});

检索事件
可以通过过滤器来检索历史事件。

const filter = {
    fromBlock: 0,
    toBlock: 'latest'
};

crowdfundingPlatform.getPastEvents('ProjectCreated', filter, (error, events) => {
    if (error) {
        console.error(error);
    } else {
        console.log(events);
    }
});
实战经验分享

在我开发的一个众筹平台项目中,事件和日志发挥了重要作用。通过定义和触发事件,我能够记录每个项目的创建、捐款和资金到位的关键操作。这些事件不仅帮助我调试和优化合约,还为前端应用提供了实时更新的能力。

例如,在 createProject 方法中,我定义了一个 ProjectCreated 事件,每当有新项目创建时,都会触发这个事件。前端应用通过监听这个事件,可以实时显示新创建的项目列表。

event ProjectCreated(uint256 indexed projectId, address indexed creator, string title, uint256 targetAmount, uint256 deadline);

function createProject(string memory title, string memory description, uint256 targetAmount, uint256 duration) public {
    projectCount++;
    uint256 deadline = block.timestamp + duration;
    projects[projectCount] = Project(msg.sender, title, description, targetAmount, 0, deadline, false);
    emit ProjectCreated(projectCount, msg.sender, title, targetAmount, deadline);
}

Solidity中的继承和接口

随着项目的复杂度增加,我遇到了一个常见的问题:代码复用。在传统的面向对象编程语言中,我们可以通过继承和接口来实现代码复用和模块化设计。那么在 Solidity 中,如何实现这一点呢?

继承:代码复用的利器

什么是继承?
在 Solidity 中,继承是一种允许一个合约继承另一个合约的功能和属性的机制。通过继承,子合约可以重用父合约的代码,从而减少重复代码,提高代码的可维护性和可读性。

单继承
最简单的继承形式是单继承,即一个子合约只继承一个父合约。下面是一个简单的例子:

// 父合约
contract Base {
    uint256 public baseValue;

    constructor(uint256 _baseValue) {
        baseValue = _baseValue;
    }

    function baseFunction() public pure returns (string memory) {
        return "Base Function";
    }
}

// 子合约
contract Child is Base {
    uint256 public childValue;

    constructor(uint256 _baseValue, uint256 _childValue) Base(_baseValue) {
        childValue = _childValue;
    }

    function childFunction() public pure returns (string memory) {
        return "Child Function";
    }
}

在这个例子中,Child 合约继承了 Base 合约。Child 合约可以访问 Base 合约的 baseValue 变量和 baseFunction 方法。

多继承

Solidity 还支持多继承,即一个子合约可以继承多个父合约。多继承可以实现更复杂的代码复用和模块化设计。下面是一个多继承的例子:

// 父合约 1
contract Base1 {
    uint256 public value1;

    constructor(uint256 _value1) {
        value1 = _value1;
    }

    function function1() public pure returns (string memory) {
        return "Function 1";
    }
}

// 父合约 2
contract Base2 {
    uint256 public value2;

    constructor(uint256 _value2) {
        value2 = _value2;
    }

    function function2() public pure returns (string memory) {
        return "Function 2";
    }
}

// 子合约
contract Child is Base1, Base2 {
    uint256 public childValue;

    constructor(uint256 _value1, uint256 _value2, uint256 _childValue) Base1(_value1) Base2(_value2) {
        childValue = _childValue;
    }

    function childFunction() public pure returns (string memory) {
        return "Child Function";
    }
}

在这个例子中,Child 合约继承了 Base1 和 Base2 合约。Child 合约可以访问 Base1 和 Base2 合约的变量和方法。

构造函数的调用顺序
在多继承的情况下,构造函数的调用顺序非常重要。Solidity 会按照继承列表从右到左的顺序调用父合约的构造函数。如果父合约之间存在依赖关系,需要特别注意构造函数的调用顺序。

contract A {
    uint256 public a;

    constructor(uint256 _a) {
        a = _a;
    }
}

contract B {
    uint256 public b;

    constructor(uint256 _b) {
        b = _b;
    }
}

contract C is A, B {
    uint256 public c;

    constructor(uint256 _a, uint256 _b, uint256 _c) A(_a) B(_b) {
        c = _c;
    }
}

在这个例子中,C 合约的构造函数会先调用 B 合约的构造函数,再调用 A 合约的构造函数。

方法重写
在继承中,子合约可以重写父合约的方法。通过重写方法,子合约可以实现不同的功能或优化父合约的行为。下面是一个方法重写的例子:

contract Base {
    function baseFunction() public pure virtual returns (string memory) {
        return "Base Function";
    }
}

contract Child is Base {
    function baseFunction() public pure override returns (string memory) {
        return "Child Function";
    }
}

在这个例子中,Child 合约重写了 Base 合约的 baseFunction 方法。virtual 关键字表示该方法可以被子合约重写,override 关键字表示当前方法是在重写父合约的方法。

接口:定义行为规范

什么是接口?
接口是一种定义合约行为规范的方式。接口不包含任何实现,只包含方法签名、事件和常量。通过接口,可以确保合约实现特定的行为,而不关心具体的实现细节。

定义接口
在 Solidity 中,接口的定义使用 interface 关键字。接口中的方法必须是 external 类型,且不能包含任何实现。下面是一个简单的接口定义:

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

在这个例子中,IERC20 接口定义了 ERC20 标准中的方法和事件。

实现接口
合约可以通过 is 关键字实现接口,并提供接口中定义的方法的具体实现。下面是一个实现 IERC20 接口的合约示例:

contract MyToken is IERC20 {
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;

    constructor(uint256 initialSupply) {
        _mint(msg.sender, initialSupply);
    }

    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public override returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, msg.sender, _allowances[sender][msg.sender] - amount);
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(_balances[sender] >= amount, "ERC20: insufficient balance");

        _balances[sender] -= amount;
        _balances[recipient] += amount;
        emit Transfer(sender, recipient, amount);
    }

    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "ERC20: mint to the zero address");

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}

在这个例子中,MyToken 合约实现了 IERC20 接口,并提供了所有方法的具体实现。

实战经验分享

在我的实际开发过程中,继承和接口发挥了重要作用。以下是一些具体的实战经验分享:

项目背景
我参与了一个去中心化金融(DeFi)项目,该项目需要实现多个不同类型的代币合约,包括标准的 ERC20 代币、可升级的代币、治理代币等。为了提高代码的可维护性和可扩展性,我们采用了继承和接口的设计模式。

使用继承实现代码复用
我们定义了一个基础的 Token 合约,包含了通用的代币逻辑,如转账、批准等。然后,我们通过继承 Token 合约,实现了不同类型的代币合约。

// 基础代币合约
contract Token {
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    function totalSupply() public view returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    function transfer(address recipient, uint256 amount) public returns (bool) {
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, msg.sender, _allowances[sender][msg.sender] - amount);
        return true;
    }

    function _transfer(address sender, address recipient, uint256 amount) internal {
        require(sender != address(0), "Token: transfer from the zero address");
        require(recipient != address(0), "Token: transfer to the zero address");
        require(_balances[sender] >= amount, "Token: insufficient balance");

        _balances[sender] -= amount;
        _balances[recipient] += amount;
        emit Transfer(sender, recipient, amount);
    }

    function _mint(address account, uint256 amount) internal {
        require(account != address(0), "Token: mint to the zero address");

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        require(owner != address(0), "Token: approve from the zero address");
        require(spender != address(0), "Token: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }
}

// 标准 ERC20 代币合约
contract StandardToken is Token {
    string public name;
    string public symbol;
    uint8 public decimals;

    constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 initialSupply) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        _mint(msg.sender, initialSupply);
    }
}

// 可升级代币合约
contract UpgradableToken is StandardToken {
    address public owner;

    constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 initialSupply) StandardToken(_name, _symbol, _decimals, initialSupply) {
        owner = msg.sender;
    }

    function upgrade(address newContract) public {
        require(msg.sender == owner, "UpgradableToken: only owner can upgrade");
        // 实现升级逻辑
    }
}

// 治理代币合约
contract GovernanceToken is StandardToken {
    mapping(address => bool) public isGovernor;

    constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 initialSupply) StandardToken(_name, _symbol, _decimals, initialSupply) {
        isGovernor[msg.sender] = true;
    }

    function addGovernor(address governor) public {
        require(isGovernor[msg.sender], "GovernanceToken: only governors can add governors");
        isGovernor[governor] = true;
    }

    function removeGovernor(address governor) public {
        require(isGovernor[msg.sender], "GovernanceToken: only governors can remove governors");
        isGovernor[governor] = false;
    }
}

通过这种方式,我们避免了大量的代码重复,提高了代码的可维护性和可扩展性。

使用接口确保行为规范
在项目中,我们还定义了一些接口,确保各个合约实现特定的行为。例如,我们定义了一个 IGovernance 接口,确保治理代币合约实现特定的治理功能。

interface IGovernance {
    function addGovernor(address governor) external;
    function removeGovernor(address governor) external;
    function isGovernor(address account) external view returns (bool);
}

然后,我们在治理代币合约中实现了这个接口:

contract GovernanceToken is StandardToken, IGovernance {
    mapping(address => bool) public isGovernor;

    constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 initialSupply) StandardToken(_name, _symbol, _decimals, initialSupply) {
        isGovernor[msg.sender] = true;
    }

    function addGovernor(address governor) public override {
        require(isGovernor[msg.sender], "GovernanceToken: only governors can add governors");
        isGovernor[governor] = true;
    }

    function removeGovernor(address governor) public override {
        require(isGovernor[msg.sender], "GovernanceToken: only governors can remove governors");
        isGovernor[governor] = false;
    }

    function isGovernor(address account) public view override returns (bool) {
        return isGovernor[account];
    }
}

通过接口,我们确保了治理代币合约实现了特定的治理功能,提高了代码的规范性和一致性,https://t.me/gtokentool  。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小版本大不同 | Navicat 17 新增 TiDB 功能

近日&#xff0c;Navicat 17 迎来了小版本更新。此次版本新增了对 PingCap 公司的 TiDB 开源分布式关系型数据库的支持&#xff0c;进一步拓展了 Navicat 的兼容边界。即日起&#xff0c;Navicat 17 所有用户可免费升级至最新版本&#xff0c;通过 Navicat 工具实现 TiDB 数据库…

SpringBoot开发——SpringBoot3.3 实现停止/重启定时任务

文章目录 一、运行效果二、项目结构三、功能实现1、项目依赖配置(pom.xml)2、配置文件(application.yaml)3、创建 TaskSchedulerProperties 配置类4、定时任务的实现5、任务管理器的实现6、控制器的实现7、启动应用程序类8、视图控制器9、前端页面(Thymeleaf + Bootstrap)…

【大数据技术基础 | 实验十一】Hive实验:新建Hive表

文章目录 一、实验目的二、实验要求三、实验原理四、实验环境五、实验内容和步骤&#xff08;一&#xff09;启动Hive&#xff08;二&#xff09;创建表&#xff08;三&#xff09;显示表&#xff08;四&#xff09;显示表列&#xff08;五&#xff09;更改表&#xff08;六&am…

c++ 后端

基础知识 1. 指针、引用2. 数组3. 缺省参数4. 函数重载5. 内联函数6. 宏7. auto8. const9. 类和对象10. 类的6个默认成员函数11. 初始化列表12. this指针13. C/C的区别14. C 三大特性15. 结构体内存对齐规则16. explicit17. static18. 友元类、友元函数19. 内部类20. 内存管理&…

[C++]:C++11(一)

1. 统一列表初始化 1.1 C11 之前的初始化方式 在 C11 标准中&#xff0c;引入了一个非常实用且强大的特性——统一列表初始化&#xff08;Uniform Initialization&#xff09;&#xff0c;它为我们在初始化各种类型的对象时提供了一种统一且方便的语法形式&#xff0c;极大地…

基于的图的异常检测算法OddBall

OddBall异常检测算法出自2010年的论文《OddBall: Spotting Anomalies in Weighted Graphs》&#xff0c;它是一个在加权图(weighted graph)上检测异常点的算法&#xff0c;基本思路为计算每一个点的一度邻域特征&#xff0c;然后在整个图上用这些特征拟合出一个函数&#xff0c…

基于AOA算术优化的KNN数据聚类算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于AOA算术优化的KNN数据聚类算法matlab仿真。通过AOA优化算法&#xff0c;搜索最优的几个特征数据&#xff0c;进行KNN聚类&#xff0c;同时对比不同个数特征下…

【模块一】kubernetes容器编排进阶实战之CoreDNS的介绍与使用

CoreDNS进阶 CoreDNS进阶-简介 DNS组件历史版本有skydns、kube-dns和coredns三个&#xff0c;k8s 1.3版本之前使用skydns&#xff0c;之后的版本到1.17及之间的版本使用kube-dns&#xff0c; 1.18开始目前主要使用coredns&#xff0c;DNS组件用于解析k8s集群中service name所对…

栈Stack和队列Queue

目录 一、栈 &#xff08;1&#xff09;用数组实现 &#xff08;2&#xff09;用单链表实现 &#xff08;3&#xff09;用标注尾结点的单链表实现 &#xff08;4&#xff09;用双向链表实现 2、栈的实际应用 &#xff08;1&#xff09;改变元素的序列 &#xff08;2&am…

ES6标准-Promise对象

目录 Promise对象的含义 Promise对象的特点 Promise对象的缺点 Promise对象的基本用法 Promise对象的简单例子 Promise新建后就会立即执行 Promise对象回调函数的参数 Promise参数不会中断运行 Promise对象的then方法 Promise对象的catch()方法 Promise状态为resolv…

【隐私计算】隐私计算的应用场景探索(大模型隐私计算、隐私数据存储计算、Web3、隐私物联网等)

1. 背景分析 隐私计算作为一种实现“原始数据不出域&#xff0c;可用不可见”的数据流通价值的关键技术&#xff0c;经历了2020-2023年的高光时刻&#xff0c;却在2024年骤然走向低谷。从各种渠道了解到一些业内曾经风光无两的隐私计算公司都有不同程度的裁员。几乎一夜之间&am…

【大数据学习 | flume】flume的概述与组件的介绍

1. flume概述 Flume是cloudera(CDH版本的hadoop) 开发的一个分布式、可靠、高可用的海量日志收集系统。它将各个服务器中的数据收集起来并送到指定的地方去&#xff0c;比如说送到HDFS、Hbase&#xff0c;简单来说flume就是收集日志的。 Flume两个版本区别&#xff1a; ​ 1&…

【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法

【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法 目录 文章目录 【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法目录摘要&#xff1a;研究背景&#xff1a;问题与挑战&#xff1a;如何解…

数据库审计工具--Yearning 3.1.9普民的使用指南

1 页面登录 登录地址:18000 &#xff08;不要勾选LDAP&#xff09; 2 修改用户密码 3 DML/DDL工单申请及审批 工单申请 根据需要选择【DML/DDL/查询】中的一种进行工单申请 填写工单信息提交SQL检测报错修改sql语句重新进行SQL检测&#xff0c;如检测失败可以进行SQL美化后…

Day44 | 动态规划 :状态机DP 买卖股票的最佳时机IV买卖股票的最佳时机III

Day44 | 动态规划 &#xff1a;状态机DP 买卖股票的最佳时机IV&&买卖股票的最佳时机III&&309.买卖股票的最佳时机含冷冻期 动态规划应该如何学习&#xff1f;-CSDN博客 本次题解参考自灵神的做法&#xff0c;大家也多多支持灵神的题解 买卖股票的最佳时机【…

Windows配置域名映射IP

一、找到 hosts 文件 打开 C:\Windows\System32\drivers\etc 二、添加hosts文件修改、写入权限 右击hosts文件&#xff0c;点击属性 -> 安全 -> Users -> 编辑 -> Users -> 添加修改、写入权限 -> 确定 -> 确定 进入常规&#xff0c;将只读属性关闭 三、…

sapiens推理的安装与使用

文章目录 1、安装1.1 克隆代码库1.2 设置 Sapiens-Lite 的代码路径1.3 创建 Conda 环境并安装必要的依赖1.4 下载模型检查点 2、推理 sapiens&#xff0c;是meta发布的以人为中心的视觉大模型&#xff0c;"sapiens"这个词来源于拉丁语&#xff0c;意为“智慧的”或“…

黑马智数Day10

项目背景说明 后台管理部分使用的技术栈是Vue2&#xff0c;前台可视化部分使用的技术栈是Vue3 前台可视化项目不是独立存在&#xff0c;而是和后台管理项目共享同一个登录页面 微前端的好处 微前端是一种前端架构模式&#xff0c;它将大型单体应用程序分解为小的、松散耦合的…

A3超级计算机虚拟机,为大型语言模型LLM和AIGC提供强大算力支持

热门大语言模型项目地址&#xff1a;www.suanjiayun.com/mirrorDetails?id66ac7d478099315577961758 近几个月来&#xff0c;我们目睹了大型语言模型&#xff08;LLMs&#xff09;和生成式人工智能强势闯入我们的视野&#xff0c;显然&#xff0c;这些模型在训练和运行时需要…

乐维网管平台(七):网络稳定与高效的“安全锦囊”

试想一下&#xff0c;你给电脑升级了一个软件&#xff0c;升级完成后发现有BUG&#xff0c;经常无故卡死&#xff0c;这时候想回退或重新安装旧版本…相对地&#xff0c;一家企业的网络管理员&#xff0c;在对公司的核心交换机进行复杂的配置调整时&#xff0c;一个小小的疏忽&…