Rust 通过 Deref trait 将智能指针当作常规引用处理

news2024/9/21 14:50:12

通过 Deref trait 将智能指针当作常规引用处理

实现 Deref trait 允许我们重载 解引用运算符dereference operator*(与乘法运算符或通配符相区别)。通过这种方式实现 Deref trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。

让我们首先看看解引用运算符如何处理常规引用,接着尝试定义我们自己的类似 Box<T> 的类型并看看为何解引用运算符不能像引用一样工作。我们会探索如何实现 Deref trait 使得智能指针以类似引用的方式工作变为可能。最后,我们会讨论 Rust 的 解引用强制转换deref coercions)功能以及它是如何处理引用或智能指针的。

我们将要构建的 MyBox<T> 类型与真正的 Box<T> 有一个很大的区别:我们的版本不会在堆上储存数据。这个例子重点关注 Deref,所以其数据实际存放在何处,相比其类似指针的行为来说不算重要。

通过解引用运算符追踪指针的值

常规引用是一种指针类型,一种理解指针的方式是将其看成指向储存在其他某处值的箭头。在示例 15-6 中,创建了一个 i32 值的引用,接着使用解引用运算符解出所引用的值:

文件名: src/main.rs

fn main() {
    let x = 5;
    let y = &x;

    assert_eq!(5, x);
    assert_eq!(5, *y);
}

示例 15-6:使用解引用运算符来解出 i32 值的引用所指向的值

变量 x 存放了一个 i325y 等于 x 的一个引用。可以断言 x 等于 5。然而,如果希望对 y 的值做出断言,必须使用 *y 来解出引用所指向的值(也就是 解引用)。一旦解引用了 y,就可以访问 y 所指向的整型值并可以与 5 做比较。

相反如果尝试编写 assert_eq!(5, y);,则会得到如下编译错误:

error[E0277]: can't compare `{integer}` with `&{integer}`
 --> src/main.rs:6:5
  |
6 |     assert_eq!(5, y);
  |     ^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}`
  |
  = help: the trait `std::cmp::PartialEq<&{integer}>` is not implemented for
  `{integer}`

不允许比较数字的引用与数字,因为它们是不同的类型。必须使用解引用运算符解出引用所指向的值。

像引用一样使用 Box<T>

可以使用 Box<T> 代替引用来重写示例 15-6 中的代码,解引用运算符也一样能工作,如示例 15-7 所示:

文件名: src/main.rs

fn main() {
    let x = 5;
    let y = Box::new(x);

    assert_eq!(5, x);
    assert_eq!(5, *y);
}

示例 15-7:在 Box<i32> 上使用解引用运算符

示例 15-7 相比示例 15-6 唯一不同的地方就是将 y 设置为一个指向 x 值的 box 实例,而不是指向 x 值的引用。在最后的断言中,可以使用解引用运算符以 y 为引用时相同的方式追踪 box 的指针。接下来让我们通过实现自己的 box 类型来探索 Box<T> 能这么做有何特殊之处。

自定义智能指针

为了体会默认情况下智能指针与引用的不同,让我们创建一个类似于标准库提供的 Box<T> 类型的智能指针。接着学习如何增加使用解引用运算符的功能。

从根本上说,Box<T> 被定义为包含一个元素的元组结构体,所以示例 15-8 以相同的方式定义了 MyBox<T> 类型。我们还定义了 new 函数来对应定义于 Box<T>new 函数:

文件名: src/main.rs

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

示例 15-8:定义 MyBox<T> 类型

这里定义了一个结构体 MyBox 并声明了一个泛型参数 T,因为我们希望其可以存放任何类型的值。MyBox 是一个包含 T 类型元素的元组结构体。MyBox::new 函数获取一个 T 类型的参数并返回一个存放传入值的 MyBox 实例。

尝试将示例 15-7 中的代码加入示例 15-8 中并修改 main 使用我们定义的 MyBox<T> 类型代替 Box<T>。示例 15-9 中的代码不能编译,因为 Rust 不知道如何解引用 MyBox

文件名: src/main.rs

fn main() {
    let x = 5;
    let y = MyBox::new(x);

    assert_eq!(5, x);
    assert_eq!(5, *y);
}

示例 15-9:尝试以使用引用和 Box<T> 相同的方式使用 MyBox<T>

得到的编译错误是:

error[E0614]: type `MyBox<{integer}>` cannot be dereferenced
  --> src/main.rs:14:19
   |
14 |     assert_eq!(5, *y);
   |                   ^^

MyBox<T> 类型不能解引用,因为我们尚未在该类型实现这个功能。为了启用 * 运算符的解引用功能,需要实现 Deref trait。

通过实现 Deref trait 将某类型像引用一样处理

如第 10 章所讨论的,为了实现 trait,需要提供 trait 所需的方法实现。Deref trait,由标准库提供,要求实现名为 deref 的方法,其借用 self 并返回一个内部数据的引用。示例 15-10 包含定义于 MyBox 之上的 Deref 实现:

文件名: src/main.rs

use std::ops::Deref;

# struct MyBox<T>(T);

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

示例 15-10:MyBox<T> 上的 Deref 实现

type Target = T; 语法定义了用于此 trait 的关联类型。关联类型是一个稍有不同的定义泛型参数的方式,现在还无需过多的担心它;第 19 章会详细介绍。

deref 方法体中写入了 &self.0,这样 deref 返回了我希望通过 * 运算符访问的值的引用。示例 15-9 中的 main 函数中对 MyBox<T> 值的 * 调用现在可以编译并能通过断言了!

没有 Deref trait 的话,编译器只会把 & 引用类型解引用。deref 方法向编译器提供了一种能力:能够获取任何实现了 Deref trait 的类型的值,并且可以通过调用这个类型的 deref 方法来获取一个解引用方法已知的 & 引用。

当我们在示例 15-9 中输入 *y 时,Rust 事实上在底层运行了如下代码:

*(y.deref())

Rust 将 * 运算符替换为先调用 deref 方法再进行普通解引用的操作,如此我们便不用担心是否还需手动调用 deref 方法了。Rust 的这个特性可以让我们写出行为一致的代码,无论是面对的是常规引用还是实现了 Deref 的类型。

deref 方法返回了一个值的引用,而 *(y.deref()) 括号外边的普通解引用仍然必须存在的原因是因为所有权。如果 deref 方法直接返回值而不是值的引用,其值(的所有权)将被移出 self。在这里以及大部分使用解引用运算符的情况下,我们并不希望获取 MyBox<T> 内部值的所有权。

注意,每次当我们在代码中使用 * 时, * 运算符都被替换成了先调用 deref 方法再接着使用 * 解引用的操作,且只会发生一次,不会对 * 操作符无限递归替换,解引用出上面 i32 类型的值就停止了,这个值与示例 15-9 中 assert_eq!5 相匹配。

函数和方法的隐式解引用强制转换

解引用强制转换deref coercions)是 Rust 在函数或方法传参上的一种便利。解引用强制转换只能工作在实现了 Deref trait 的类型上。解引用强制转换将一种类型(A)隐式转换为另外一种类型(B)的引用,因为 A 类型实现了 Deref trait,并且其关联类型是 B 类型。比如,解引用强制转换可以将 &String 转换为 &str,因为类型 String 实现了 Deref trait 并且其关联类型是 str。代码如下:

#[stable(feature = "rust1", since = "1.0.0")]
impl ops::Deref for String {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}

当我们将特定类型的值的引用作为参数传递给函数或方法,但是被传递的值的引用与函数或方法中定义的参数类型不匹配时,会发生解引用强制转换。这时会有一系列的 deref 方法被调用,把我们提供的参数类型转换成函数或方法需要的参数类型。

解引用强制转换的加入使得 Rust 开发者编写函数和方法调用时无需增加过多显式使用 &* 的引用和解引用。这个功能也使得我们可以编写更多同时作用于引用或智能指针的代码。

作为展示解引用强制转换的实例,让我们使用示例 15-8 中定义的 MyBox<T>,以及示例 15-10 中增加的 Deref 实现。示例 15-11 展示了一个有着字符串 slice 参数的函数定义:

文件名: src/main.rs

fn hello(name: &str) {
    println!("Hello, {}!", name);
}

示例 15-11:hello 函数有着 &str 类型的参数 name

可以使用字符串 slice 作为参数调用 hello 函数,比如 hello("Rust");。解引用强制转换使得用 MyBox<String> 类型值的引用调用 hello 成为可能,如示例 15-12 所示:

文件名: src/main.rs

# use std::ops::Deref;
#
# struct MyBox<T>(T);
#
# impl<T> MyBox<T> {
#     fn new(x: T) -> MyBox<T> {
#         MyBox(x)
#     }
# }
#
# impl<T> Deref for MyBox<T> {
#     type Target = T;
#
#     fn deref(&self) -> &T {
#         &self.0
#     }
# }
#
# fn hello(name: &str) {
#     println!("Hello, {}!", name);
# }
#
fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m);
}

示例 15-12:因为解引用强制转换,使用 MyBox<String> 的引用调用 hello 是可行的

这里使用 &m 调用 hello 函数,其为 MyBox<String> 值的引用。因为示例 15-10 中在 MyBox<T> 上实现了 Deref trait,Rust 可以通过 deref 调用将 &MyBox<String> 变为 &String。标准库中提供了 String 上的 Deref 实现,其会返回字符串 slice,这可以在 Deref 的 API 文档中看到。Rust 再次调用 deref&String 变为 &str,这就符合 hello 函数的定义了。

如果 Rust 没有实现解引用强制转换,为了使用 &MyBox<String> 类型的值调用 hello,则不得不编写示例 15-13 中的代码来代替示例 15-12:

文件名: src/main.rs

# use std::ops::Deref;
#
# struct MyBox<T>(T);
#
# impl<T> MyBox<T> {
#     fn new(x: T) -> MyBox<T> {
#         MyBox(x)
#     }
# }
#
# impl<T> Deref for MyBox<T> {
#     type Target = T;
#
#     fn deref(&self) -> &T {
#         &self.0
#     }
# }
#
# fn hello(name: &str) {
#     println!("Hello, {}!", name);
# }
#
fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&(*m)[..]);
}

示例 15-13:如果 Rust 没有解引用强制转换则必须编写的代码

(*m)MyBox<String> 解引用为 String。接着 &[..] 获取了整个 String 的字符串 slice 来匹配 hello 的签名。没有解引用强制转换所有这些符号混在一起将更难以读写和理解。解引用强制转换使得 Rust 自动的帮我们处理这些转换。

当所涉及到的类型定义了 Deref trait,Rust 会分析这些类型并使用任意多次 Deref::deref 调用以获得匹配参数的类型。这些解析都发生在编译时,所以利用解引用强制转换并没有运行时损耗!

解引用强制转换如何与可变性交互

类似于使用 Deref trait 重载不可变引用的 * 运算符,Rust 提供了 DerefMut trait 用于重载可变引用的 * 运算符。

Rust 在发现类型和 trait 的实现满足以下三种情况时会进行解引用强制转换:

  • T: Deref<Target=U> :从 &T&U
  • T: DerefMut<Target=U> :从 &mut T&mut U
  • T: Deref<Target=U> :从 &mut T&U

前两种情况除了可变性之外是相同的:第一种情况表明如果有一个 &T,而 T 实现了返回 U 类型的 Deref,则可以直接得到 &U。第二种情况表明对于可变引用也有着相同的行为。

第三种情况有些微妙:Rust 也会将可变引用强转为不可变引用,但是反之是 不可能 的,因为不可变引用永远也不能强转为可变引用。因为根据借用规则,如果有一个可变引用,其必须是这些数据的唯一引用(否则程序将无法编译)。将一个可变引用转换为不可变引用永远也不会打破借用规则。将不可变引用转换为可变引用则需要数据只能有一个不可变引用,而借用规则无法保证这一点。因此,Rust 无法假设将不可变引用转换为可变引用是可能的。

推荐几款学习编程的免费平台

免费在线开发平台(https://docs.ltpp.vip/LTPP/)

       探索编程世界的新天地,为学生和开发者精心打造的编程平台,现已盛大开启!这个平台汇集了近4000道精心设计的编程题目,覆盖了C、C++、JavaScript、TypeScript、Go、Rust、PHP、Java、Ruby、Python3以及C#等众多编程语言,为您的编程学习之旅提供了一个全面而丰富的实践环境。       
      在这里,您不仅可以查看自己的代码记录,还能轻松地在云端保存和运行代码,让编程变得更加便捷。平台还提供了私聊和群聊功能,让您可以与同行们无障碍交流,分享文件,共同进步。不仅如此,您还可以通过阅读文章、参与问答板块和在线商店,进一步拓展您的知识边界。
       为了提升您的编程技能,平台还设有每日一题、精选题单以及激动人心的编程竞赛,这些都是备考编程考试的绝佳资源。更令人兴奋的是,您还可以自定义系统UI,选择视频或图片作为背景,打造一个完全个性化的编码环境,让您的编程之旅既有趣又充满挑战。

免费公益服务器(https://docs.ltpp.vip/LTPP-SHARE/linux.html)

       作为开发者或学生,您是否经常因为搭建和维护编程环境而感到头疼?现在,您不必再为此烦恼,因为一款全新的免费公共服务器已经为您解决了所有问题。这款服务器内置了多种编程语言的编程环境,并且配备了功能强大的在线版VS Code,让您可以随时随地在线编写代码,无需进行任何复杂的配置。
随时随地,云端编码
       无论您身在何处,只要有网络连接,就可以通过浏览器访问这款公共服务器,开始您的编程之旅。这种云端编码的便利性,让您的学习或开发工作不再受限于特定的设备或环境。
丰富的编程语言支持
       服务器支持包括C、C++、JavaScript、TypeScript、Go、Rust、PHP、Java、Ruby、Python3以及C#等在内的多种主流编程语言,满足不同开发者和学生的需求。无论您是初学者还是资深开发者,都能找到适合自己的编程环境。
在线版VS Code,高效开发
       内置的在线版VS Code提供了与本地VS Code相似的编辑体验,包括代码高亮、智能提示、代码调试等功能,让您即使在云端也能享受到高效的开发体验。
数据隐私和安全提醒
       虽然服务器是免费的,但为了保护您的数据隐私和安全,我们建议您不要上传任何敏感或重要的数据。这款服务器更适合用于学习和实验,而非存储重要信息。

免费公益MYSQL(https://docs.ltpp.vip/LTPP-SHARE/mysql.html)

       作为一名开发者或学生,数据库环境的搭建和维护往往是一个复杂且耗时的过程。但不用担心,现在有一款免费的MySQL服务器,专为解决您的烦恼而设计,让数据库的使用变得简单而高效。
性能卓越,满足需求
       虽然它是免费的,但性能绝不打折。服务器提供了稳定且高效的数据库服务,能够满足大多数开发和学习场景的需求。
在线phpMyAdmin,管理更便捷
       内置的在线phpMyAdmin管理面板,提供了一个直观且功能强大的用户界面,让您可以轻松地查看、编辑和管理数据库。
数据隐私提醒,安全第一
       正如您所知,这是一项公共资源,因此我们强烈建议不要上传任何敏感或重要的数据。请将此服务器仅用于学习和实验目的,以确保您的数据安全。

免费在线WEB代码编辑器(https://docs.ltpp.vip/LTPP-WEB-IDE/)

       无论你是开发者还是学生,编程环境的搭建和管理可能会占用你宝贵的时间和精力。现在,有一款强大的免费在线代码编辑器,支持多种编程语言,让您可以随时随地编写和运行代码,提升编程效率,专注于创意和开发。
多语言支持,无缝切换
       这款在线代码编辑器支持包括C、C++、JavaScript、TypeScript、Go、Rust、PHP、Java、Ruby、Python3以及C#在内的多种编程语言,无论您的项目需要哪种语言,都能在这里找到支持。
在线运行,快速定位问题
       您可以在编写代码的同时,即时运行并查看结果,快速定位并解决问题,提高开发效率。
代码高亮与智能提示
       编辑器提供代码高亮和智能提示功能,帮助您更快地编写代码,减少错误,提升编码质量。

免费二维码生成器(https://docs.ltpp.vip/LTPP-QRCODE/)

       二维码(QR Code)是一种二维条码,能够存储更多信息,并且可以通过智能手机等设备快速扫描识别。它广泛应用于各种场景,如:
企业宣传
       企业可以通过二维码分享公司网站、产品信息、服务介绍等。
活动推广
       活动组织者可以创建二维码,参与者扫描后可以直接访问活动详情、报名链接或获取电子门票。
个人信息分享
       个人可以生成包含联系方式、社交媒体链接、个人简历等信息的二维码。
电子商务
       商家使用二维码进行商品追踪、促销活动、在线支付等。
教育
       教师可以创建二维码,学生扫描后可以直接访问学习资料或在线课程。
交通出行
       二维码用于公共交通的票务系统,乘客扫描二维码即可进出站或支付车费。        功能强大的二维码生成器通常具备用户界面友好,操作简单,即使是初学者也能快速上手和生成的二维码可以在各种设备和操作系统上扫描识别的特点。

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

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

相关文章

WPF+MvvmLight 项目入门完整教程(一)

WPF+MvvmLight入门完整教程一 创建项目MvvmLight框架安装完善整个项目的目录结构创建自定义的字体资源下载更新和使用字体资源创建项目 打开VS2022,点击创建新项目,选择**WPF应用(.NET Framework)** 创建一个名称为 CommonProject_DeskTop 的项目,如下图所示:MvvmLight框架…

SQL Server 2019安装详细教程(图文详解,非常靠谱)

Microsoft SQL Server 是一种关系数据库管理系统 (RDBMS)。 应用程序和工具连接到 SQL Server 实例或数据库&#xff0c;并使用 Transact-SQL (T-SQL) 进行通信。 SQL Server Management Studio (SSMS) 是一种集成环境&#xff0c;用于管理任何 SQL 基础结构。 使用 SSMS 访问、…

AutoGen实战AI Agent开发

最近&#xff0c;关于 AI 智能体或AI代理&#xff08;AI Agent&#xff09;的讨论很多。受这种炒作的影响&#xff0c;我阅读了一些资料&#xff0c;偶然发现了 AutoGen&#xff0c;这是一个构建 AI 代理的很棒的库。但 AI 代理到底是什么&#xff1f;为什么它们很重要&#xf…

移动UI:表单美观易操作的十大准则,非常实用。

移动端表单设计需要遵循一些原则&#xff0c;以确保美观和易操作&#xff1a; 1.简洁明了&#xff1a; 在移动端表单设计中&#xff0c;要尽量简化内容&#xff0c;避免过多的文字和元素&#xff0c;保持简洁明了的布局&#xff0c;以便用户能够快速理解并填写表单。 2.响应式…

在 PostgreSQL 中如何优化涉及多个视图嵌套和函数调用的复杂查询?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 在 PostgreSQL 中如何优化涉及多个视图嵌套和函数调用的复杂查询一、理解问题的本质二、优化的基本原则…

直播美颜工具开发教学:视频美颜SDK集成详解

本篇文章&#xff0c;笔者将详细介绍如何在直播应用中集成视频美颜SDK&#xff0c;让你的直播画面焕然一新。 一、什么是视频美颜SDK&#xff1f; 视频美颜SDK是一种软件开发工具包&#xff0c;提供了视频处理和图像增强功能。通过集成视频美颜SDK&#xff0c;开发者可以轻松…

【postgresql】角色(Role)

PostgreSQL 中&#xff0c;角色&#xff08;Role&#xff09;是一个重要的概念&#xff0c;用于管理数据库的访问权限和用户身份。角色可以被视为用户或组&#xff0c;具体取决于它们的配置方。 角色属性 角色可以具有多种属性&#xff1a; LOGIN&#xff1a;允许角色登录数据…

Apple Vision Pro 和其商业未来

机器人、人工智能相关领域 news/events &#xff08;专栏目录&#xff09; 本文目录 一、Vision Pro 生态系统二、Apple Vision Pro 的营销用例 随着苹果公司备受期待的进军可穿戴计算领域&#xff0c;新款 Apple Vision Pro 承载着巨大的期望。 苹果公司推出的 Vision Pro 售…

2024.7.12单片机PWM

遇到了一个光标变成下划线的问题&#xff1a; Keil5光标变下划线&#xff0c;变回来的方法_keil5光标是下划线-CSDN博客 这里是用了输入捕获&#xff08;IC&#xff1a;input capture&#xff09;&#xff0c;输出比较&#xff08;OC:Output Compare&#xff09;区别 学到这…

[Linux]添加sudoers

之前我们讲过sudo这个命令,它可以让我们普通用户进行短暂的提权,上回我们讲完了vim 本篇是个短篇,目的就是让我们之后的学习中可以使用sudo命令。 首先我们先登录root用户 ls /etc/sudoer 我们需要改的就是上面的这个文件 vim /etc/sudoers 我们用vim打开 把光标移动到这…

17-8 向量数据库之野望8 - 7 个主流向量数据库

​​​​​​ 在快速发展的人工智能 (AI)、机器学习 (ML) 和数据工程领域,对高效数据存储和检索系统的需求至关重要。矢量数据库已成为管理这些技术通常依赖的复杂高维数据的关键解决方案。在这里,我们探讨了每个 AI/ML/数据工程师都应该熟悉的七个矢量数据库,重点介绍了它们…

mysql-connector-java 8.0.33 反序列化漏洞

前言 经过与oracle官方沟通&#xff0c;在最新的mysql-connector-j 9.0.0里不存在这个问题&#xff0c;所以他们不认为这是个漏洞 不过确实&#xff0c;mysql-connector-java这个分支已经迁移到mysql-connector-j了&#xff0c;当时没注意&#xff0c;交的时候只注意了mysql-c…

全球DeepFake攻防挑战赛DataWhale AI 夏令营——图像赛道

全球DeepFake攻防挑战赛&DataWhale AI 夏令营——图像赛道 赛题背景 随着人工智能技术的迅猛发展&#xff0c;深度伪造技术&#xff08;Deepfake&#xff09;正成为数字世界中的一把双刃剑。这项技术不仅为创意内容的生成提供了新的可能性&#xff0c;同时也对数字安全构…

数学建模·模糊评价法

模糊评价法 一种解决评价问题或者得出最佳方案的方法 主观性仍比较强 具体定义 三集&#xff1a;因素集&#xff0c;评语集和权重集&#xff0c;通过模拟矩阵的处理得到最合理的评语 具体步骤 因素集 因素集的确定不难&#xff0c;难在对分级评价时&#xff0c;对因素集的分级…

MYSQL--第九次作业

MYSQL–第九次作业 1、安装redis&#xff0c;启动客户端、验证。 安装网址&#xff1a;Redis Released&#xff0c;找到适合自己电脑的redis版本后&#xff0c;下载并安装。 安装完后&#xff0c;打开cmd命令框&#xff1a; -- 启动客户端 C:\Windows\System32>redis-cl…

AV1 编码标准帧间预测技术概述

AV1 编码标准帧间预测 AV1&#xff08;AOMedia Video1&#xff09;是一种开源的视频编码格式&#xff0c;它在帧间预测技术上做出了显著的改进和扩展&#xff0c;以提供比现有标准更高的压缩效率和更好的视频质量。以下是AV1帧间预测技术的几个关键点&#xff1a; 参考帧扩展&a…

STM32(五):STM32指南者-按键控制灯开关实验

说明&#xff1a;源代码和教程可从野火处下载&#xff0c;本博客为了记录学习过程STM32&#xff08;四&#xff09;&#xff1a;STM32指南者-跑马灯实验的基础上 一、采用轮询方式1、bsp_key.h2、bsp_key.c3、main.c 二、采用中断方式1、bsp_exti.h2、bsp_exti.c3、stm32f10x_i…

【动态规划1】斐波那契数列模型篇

文章目录 声明动态规划介绍1137.第N个泰波那契数题目描述分析代码 面试题 08.01. 三步问题题目描述分析代码 746.使用最小花费爬楼梯题目描述分析代码 91.解码⽅法题目描述分析代码 声明 本篇博客为动态规的基础篇&#xff0c;从零开始学习动态规划&#xff0c;如有错误&#…

14、Python之super star:一颗星、两颗星,满天都是小星星

引言 关于Python系列的文章&#xff0c;已经通过两篇文章&#xff0c;介绍了Python中关于函数的简单使用&#xff0c;包括为什么要使用函数&#xff0c;以及函数中带默认值参数的使用注意事项。 之后&#xff0c;岔开函数的主题&#xff0c;通过几篇番外篇&#xff0c;重点谈…

PHP恋爱话术微信小程序系统源码

&#x1f496;恋爱高手的秘密武器&#xff01;恋爱话术微信小程序&#xff0c;让情话信手拈来✨ &#x1f4ad;【开场白&#xff1a;恋爱路上的甜蜜助手】&#x1f4ad; 还在为跟心仪的TA聊天时找不到话题而尴尬&#xff1f;或是担心自己说的每句话都显得那么“直男/女”&…