Rust学习日记(二)变量的使用--结合--温度换算/斐波那契数列--实例

前言:
这是一个系列的学习笔记,会将笔者学习Rust语言的心得记录。
当然,这并非是流水账似的记录,而是结合实际程序项目的记录,如果你也对Rust感兴趣,那么我们可以一起交流探讨,使用Rust来构建程序。

注:本文中使用Rust都是在windows环境下,如果是macOS或者linux,其指令或有不同,请注意。

系列第一篇:Rust学习日记(一)Cargo的使用

概述:

这是Rust学习笔记的第二篇,主要说一下Rust中的变量,本文将结合两个实例来说明,这两个实例分别是:
1、在华氏度和摄氏度之间转换温度
2、生成指定个数(n)的斐波那契数列
如果你看过rust的官方手册,那你会熟悉,这两个例子事实上就是官方留的“课后习题”

在进入实例之前,还是要先来简单看看变量的使用。

变量的申明

1、关键词let
在rust中,申明变量的关键词就是let。如果你使用过其他语言,那么你可能对rust的变量申明有疑问,为什么用let?
我觉得不用纠结,这就是一个定义,使用什么单词来作为关键词并不重要,重要的是,这个关键词的作用。

fn main() {
    let x=5;
    println!("x is {x}");
}

上面的示例程序段,是在主函数中创建了变量x,并打印,在终端输入cargo run,输出如下:

PS D:\008 rustpro\var> cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target\debug\var.exe`
x is 5

2、变量的可变与不可变
变量,顾名思义,就是可变化的量,不可变的是变量吗?不可变的应该是常量吧?
在rust中,默认情况下,变量是不可变的。
这是rust的设计,它的目的,当然是为了安全。“安全”是贯穿rust整个体系的,后面会有更多更复杂更高级的功能,都会和它有关。

fn main() {
    let x=5;
    println!("x is {x}");
    x=x+1;
    println!("x is {x}");
}

对前面的函数稍作修改,给变量x再次赋值,再看输出:

PS D:\008 rustpro\var> cargo run
   Compiling var v0.1.0 (D:\008 rustpro\var)
error[E0384]: cannot assign twice to immutable variable `x`
 --> src\main.rs:4:5
  |
2 |     let x=5;
  |         -
  |         |
  |         first assignment to `x`
  |         help: consider making this binding mutable: `mut x`
3 |     println!("x is {x}");
4 |     x=x+1;
  |     ^^^^^ cannot assign twice to immutable variable

For more information about this error, try `rustc --explain E0384`.
error: could not compile `var` (bin "var") due to previous error

错误提示,cannot assign twice to immutable variable,不能给“immutable variable”分配两次,什么叫immutable variable?就是不可变变量。
所以,现在你就知道,rust是比较奇葩的了。
但是,这就是rust的设计,如果你定义变量的时候,只是简单的let x=5;那么这个x是不可变的。那如果要可变呢:

let mut x=5

再加一个关键词mut,mut其实就是mutable的简写,可变的。

fn main() {
    let mut x=5;
    println!("x is {x}");
    x=x+1;
    println!("x is {x}");
}

cargo run:

PS D:\008 rustpro\var> cargo run
   Compiling var v0.1.0 (D:\008 rustpro\var)
    Finished dev [unoptimized + debuginfo] target(s) in 0.43s
     Running `target\debug\var.exe`
x is 5
x is 6

可以发现,“变量x”的值可以多次分配,也就是真正的可变了。
看到这里你发现什么?
你会发现,在rust里,你对一个对象,比如变量的定义,必须要十分清楚,可变就可变,不可变就不可变,
这样一来,rust编译器就能非常清楚地知道,你定义这个变量的目的,它会编译过程对你的意图全程跟踪,
不可变变量如果不小心让它变化,编译器就会报错,提示你,对你来说,这就是安全。
你不用等程序都运行了,然后偶尔崩溃了一次,然后去找bug,找来找去无论怎么编译就是没问题,但一运行就报错,这是令人崩溃的。

你已经了解到rust中变量的奇葩性了,我们接着再看几个,然后再进入实例。

3、常量
rust中常量用const来申明,这个const应该是比较熟悉的单词了,很多编程语言申明常量都是用它吧。
const也就是Constants的简写。
看一下申明的格式吧:

const C_1=5

如果你真的这么写,cargo check一下,会报错:

PS D:\008 rustpro\var> cargo check
    Checking var v0.1.0 (D:\008 rustpro\var)
error: missing type for `const` item
 --> src\main.rs:6:14
  |
6 |     const C_1=5;
  |              ^ help: provide a type for the constant: `: i32`

error: could not compile `var` (bin "var") due to previous error

rust编译器提示你,需要给常量提供一个数据类型,并且给了你一个选项:i32
rust中的数据类型----整数型:
在这里插入图片描述
关于数据类型,其实我认为不用多说,照着手册看看了解一下即可,因为一般来说,对编程有所了解的,对数据类型一般都不陌生,可以说,几乎所有语言中的数据类型,都大同小异。
常见的比如说整数、浮点数、布尔量、字符串等,还有一些复杂的,比如结构体(复合体)、数组、列表、元组等。

我们接着说常量,上面说这样写const C_1=5;会报错,原因是未给常量提供数据类型,所以我们应该这么来申明常量

const C_1:i32=5

rust就是这样,它要求你明确,哪怕是常量,它不会为你隐式转换,它要求你定义常量时,明确常量的数据类型。
到此,你会发现,rust似乎很“麻烦”,这也要明确,那也要明确,为什么不能像其他语言一样,直接a=5;或者const a=5;
我只能说,rust它就是这样一门语言,如果你对此实在不能接受,那么放弃是好的,如果无所谓,那么就按照rust的规则,暂时先走下去。

4、变量的覆盖
我们在本文前面说过,rust中的变量有可变和不可变的区别,直接申明变量,默认不可变,如果要可变,需加关键词mut。
但现在我们来看另一种使用:

fn main() {
  let x=5;
  println!("x is {x}");
  let x:char='a';
  println!("x is {x}");
}

我们在第一行定义x变量,且赋值为5,但没有用mut使其可变,所以x是不可变的,但是我们在第三行再次使用x变量,而且给它赋值为字符‘a’。
我们先cargo run一下:

PS D:\008 rustpro\var> cargo run
   Compiling var v0.1.0 (D:\008 rustpro\var)
    Finished dev [unoptimized + debuginfo] target(s) in 0.63s
     Running `target\debug\var.exe`
x is 5
x is a

可以看到,rust正常编译,且结果x的值被改变了。你应该看到区别,就是第三行再次使用x时,重新定义了一次。
rust中,用let来定义变量,是可以重名的,并且后一个可以覆盖前面的,所谓“shadow”,即旧的变量活在新的变量的阴影里
你可能会觉得,这样是不是会造成困扰,如果不小心使用了一样的变量名,覆盖之前的变量数据,岂不是会影响程序。但这个问题容易解决,只需要你在命名变量时有自己的规律即可,但是rust中这样的机制会带来好处。
我们看到,同样的变量x,在第一次和第二次的定义中,不仅值可以不同,连数据类型也可以不同。
什么场景会这样使用,比如你要输入一个值,是字符类型,但程序接收字符后,要以整数型来运算,但是这两个其实一个东西,如果是其他语言,你可能会这样定义变量,x_str和x_int,但是rust中你可以只定义一个x,

let x:char='a';
let x:i32=5;

好了,关于变量就先说到这,基本上差不多了,当然这并非是说把变量的所有方面都涉及到了,只是说基本的概念都已经了解了。
下面将开始实例:

一、温度转换

1、新建一个temp项目

PS D:\008 rustpro> cargo new temp
     Created binary (application) `temp` package
PS D:\008 rustpro> cd temp
PS D:\008 rustpro\temp> cargo build
   Compiling temp v0.1.0 (D:\008 rustpro\temp)
    Finished dev [unoptimized + debuginfo] target(s) in 0.49s
PS D:\008 rustpro\temp> 

在这里插入图片描述
新建项目后,rust会自动生成一个main.rs主函数。
这个主函数里默认的是打印“hello world”,我们要修改它,以达到温度转换的功能。
华氏度和摄氏度的转换关系如下:

1 摄氏度=33.8 华氏度

所以,如果我们输入一个摄氏度值为x,那么转换为华氏度应该是:f(x)=33.8*x
数学关系很清楚,是很简单的一元一次函数。
先看一下main.rs的结构:

fn main() {
    println!("Hello, world!");
}

fnlet一样,是关键词,定义函数,所以fn后面的main就是函数,只不过它是主函数,所以地位特殊,但是,main这个名字,想必大家也不陌生,无论是C还是python,都有主函数,地位也差不多,谁让它是main呢!
但我们先不修改main,我们先新建一个函数,就叫temp_convert:

fn temp_convert(){
    
}

添加逻辑代码:

fn temp_convert(t:f32)->f32{
    let t2=t*33.8;
    return t2;
}

可以看到,我们在新建的函数基础上,添加了一个形式参数t,把它的数据类型定义为f32,即浮点数。
定义了一个函数返回值类型:->f32,即函数返回值也是f32类型。
然后在函数体内,定义了变量t2,其赋值为t*33.8,
最后将计算后的t2的值返回。
这个函数中形式参数t即输入的摄氏度,返回的t2即转换的华氏度。
接下来,就是在主程序里调用了:
修改一下main函数:

fn main() {
    let t1=temp_convert(3.0);
    println!("t_f is {t1}");
}

因为函数temp_convert有返回值,所以我们定义一个变量t1来储存它。
到此,简单的温度转换就实现了,只要给函数的参数赋予不同的值(摄氏度),函数就可以返回华氏度:

PS D:\008 rustpro\temp> cargo run
   Compiling temp v0.1.0 (D:\008 rustpro\temp)
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s
     Running `target\debug\temp.exe`
t_f is 101.399994

但这样肯定不算是一个完整的程序,我们设想应该是这样,程序启动,会输出一个文字提示,让我们输入一个摄氏度,我们输入后,程序输出一个华氏度。然后能循环,这样才算是完整的转换程序。
我们分两部分来说,一是如何接受用户输入,二是如何循环。

1 用户输入

用户输入,需要用到IO接口,在rust中即std::io
std::io,这是一个程序包crate,类似于其他程序的模块。在使用它之前,需要先导入它。

use std::io;

然后使用它:

use std::io;
fn main() {
    let mut t_in=String::new();
    println!("请输入摄氏度值:");
    io::stdin()
        .read_line(&mut t_in)
        .expect("error");
    let t_in:f32=t_in.trim().parse().expect("error");
    let t1=temp_convert(t_in);
    println!("华氏度是 {t1}");
}

上面的主函数main变化较大,我们一一来说一下。我们使用use关键词导入了std::io,这是用来接受用户输入的模块。
然后在主函数体内添加程序:

let mut t_in=String::new();

这行是定义了一个可变变量t_in且类型为string

  io::stdin()
        .read_line(&mut t_in)
        .expect("error");

这行就是io接口的调用,用于读取用户的输入,read_line即是std::io模块的函数。&mut t_in是将我们定义的变量t_in来接受read_line的输入值。
其实上面这行直接写如下:

io::stdin().read_line(&mut t_in).expect("error");

之所以写成多行样式,是为了便于阅读。
这里的expect是对潜在错误的处理,关于错误的处理不在本文的范围,后续会有专门叙述。此处只要知道这样用就可以了。
我们现在已经从用户处接受到输入值,即t_in,接下来要对输入值进行处理:

let t_in:f32=t_in.trim().parse().expect("error");

此处我们使用前面介绍的变量覆盖,先定义了t_in为用户输入,且数据类型为string类型,接着此行用同名变量t_in定义为f32类型,覆盖之前的字符串类型。
其中的trim是消除一个字符串的开头和结尾的多余空格,parse是将字符串转为另一种数据类型,在此处就是转为f32类型。expect我们已经说过,是对返回值的判断时可能发生的错误处理。

`let t1=temp_convert(t_in);

在顺利获得用户输入的值且转为浮点类型后,我们调用写好的temp_convert转换函数,其形式参数t在调用时传入实际参数t_in。将temp_convert函数的返回值,传给变量t1。
完整程序:

use std::io;
fn main() {
    let mut t_in=String::new();
    println!("请输入摄氏度值:");
    io::stdin()
        .read_line(&mut t_in)
        .expect("error");
    let t_in:f32=t_in.trim().parse().expect("error");
    let t1=temp_convert(t_in);
    println!("华氏度是 {t1}");
}
fn temp_convert(t:f32)->f32{
    let t2=t*33.8;
    return t2;
}

在终端运行cargo run:

PS D:\008 rustpro\temp> cargo run
   Compiling temp v0.1.0 (D:\008 rustpro\temp)
    Finished dev [unoptimized + debuginfo] target(s) in 0.74s
     Running `target\debug\temp.exe`
请输入摄氏度值:
2
华氏度是 67.6

好了,第一部分完成了,现在我们能成功根据输入的摄氏度转为华氏度且输出,但缺点是每次运行只能进行一次,所以需要循环。

循环

想让程序循环起来,也很简单,使用loop指令,它会循环执行主程序的逻辑,直到触发停止。本文中未添加明确的停止程序,因为这不在我们的学习范围,后续会有说明,此文不赘述。

fn main() {
    loop{
        let mut t_in=String::new();
        println!("请输入摄氏度值:");
        io::stdin()
            .read_line(&mut t_in)
            .expect("error");
        let t_in:f32=t_in.trim().parse().expect("error");
        let t1=temp_convert(t_in);
        println!("华氏度是 {t1}");
    }
   
}

我们可以看到,只需要将main中的原程序添加进loop的{}中即可。
让我们运行一下:

PS D:\008 rustpro\temp> cargo run
   Compiling temp v0.1.0 (D:\008 rustpro\temp)
    Finished dev [unoptimized + debuginfo] target(s) in 0.46s
     Running `target\debug\temp.exe`
请输入摄氏度值:
2
华氏度是 67.6
请输入摄氏度值:
3
华氏度是 101.399994
请输入摄氏度值:
4
华氏度是 135.2
请输入摄氏度值:
1
华氏度是 33.8
请输入摄氏度值:
2
华氏度是 67.6
请输入摄氏度值:
r
thread 'main' panicked at src\main.rs:10:42:
error: ParseFloatError { kind: Invalid }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\temp.exe` (exit code: 101)

可以看到,程序在不停地执行,如果用户一直输入正确的值得话,但你会发现在终端的记录中,最后输入r时,rust报错且退出了,这是因为程序中没有对非法输入的应对措施,当输入非法值时,只能报错退出。
但我不准备在本文中完善它,我相信随着我们学习rust的深入,这个问题会迎刃而解。
如果你看到这里,我相信你对rust中变量、函数的使用,有了初步的印象了。
下面我们说一下第二个实例,即实现斐波那契数列。

斐波那契数列

斐波那契数列应该是比较经典的练手题目了,你在学习其他语言如C、python、java中都可以通过编写斐波那契数列来加深对语言的理解。
rust也不例外,我在文章一开头就说过,此文所列举的两个实例,都是rust官方手册中给读者留的课后习题。
其实官方在手册的第二章给了一个很好的实例即猜谜游戏,说的非常详细,但我们作为学习者,在学习了官方手册后,自己要学会应用,所以我选择用课后习题来作为示例。

斐波那契数列是这样的数列:

1,1,2,3,5,8,13,21,34,55,89…

这个数列的规律是,从第三项开始,每一项的值都是它前面两项的和。用公式表示为:

f(n)=f(n-1)+f(n-2);n>2

所以,如果要用函数来实现斐波那契数列,函数肯定是递归的。

rust实现,首先是创建新项目,这个不用多说了。
在这里插入图片描述
接着创建新的函数,命名为fibonacci

fn fibonacci(num:i32)->i32{
   if num<3{
    return 1;
   }
   else{
    return fibonacci(num-1)+fibonacci(num-2);
   }
}

这个fibonacci函数有个形式参数num,数据类型为i32,函数的返回值类型由’->'符号指出,也是i32
函数体内的逻辑,是对num进行判断,如果num小于3,也就是fibonacci数列的前2项,那么函数返回值固定为1,如果num大于等于3,那么返回值就等于前2项之和。
此处的判断,使用了if。。else语句。if else应该不用多说了吧,这应该是学习编程的人,都会了解的。
几乎你学习任何编程语言,都会遇到if语句。
所以,本文也不会赘述if。。else语句。
接下来看main函数:

use std::io;
fn main() {
    loop{
        println!("please enter fibonacci number:");
        let mut number=String::new();
        io::stdin()
            .read_line(&mut number)
            .expect("err");
        let number:i32=number.trim().parse().expect("err");
        println!("number is {number}");
        for y in 1..number{
            let x=fibonacci(y);
            println!("fibo({y}) is {x}");
        }
    }  
    
}

可以看到,几乎和前面的实例一样,一点区别在于子函数fibonacci的调用处,这里用了for语句:
之所以使用for语句,因为斐波那契数列是一个数列,当我们给定一个斐波那契数列的项索引(n)时,我们实际上希望程序能够列出至n项的斐波那契数列所有的项。

 for y in 1..number{
            let x=fibonacci(y);
            println!("fibo({y}) is {x}");
        }

同理,for循环也是常见的,和if语句一样,我们会在后续的文章里统一说明,此文不赘述。
我们还是看一下实际运行效果吧:

PS D:\008 rustpro\fibonacci> cargo run
   Compiling fibonacci v0.1.0 (D:\008 rustpro\fibonacci)
    Finished dev [unoptimized + debuginfo] target(s) in 0.71s
     Running `target\debug\fibonacci.exe`
please enter fibonacci number:
2
number is 2
fibo(1) is 1
please enter fibonacci number:
5
number is 5
fibo(1) is 1
fibo(2) is 1
fibo(3) is 2
fibo(4) is 3
please enter fibonacci number:
10
number is 10
fibo(1) is 1
fibo(2) is 1
fibo(3) is 2
fibo(4) is 3
fibo(5) is 5
fibo(6) is 8
fibo(7) is 13
fibo(8) is 21
fibo(9) is 34
please enter fibonacci number:
r
thread 'main' panicked at src\main.rs:10:46:
err: ParseIntError { kind: InvalidDigit }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\fibonacci.exe` (exit code: 101)

总结一下,rust变量:

let x=5;//x不可变
let mut x=5;//x可变
let x=5;
let x='a';//x依旧不可变,但可以被重名者覆盖,且可不同数据类型
const x:i32=5;//常量需指定数据类型

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

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

相关文章

国外住宅IP代理选择的8个方法,稳定的海外IP哪个靠谱?

一、国外住宅IP代理是什么&#xff1f; 代理服务器充当您和互联网之间的网关。它是一个中间服务器&#xff0c;将最终用户与他们浏览的网站分开。如果您使用国外代理IP&#xff0c;互联网流量将通过国外代理服务器流向您请求的地址。然后&#xff0c;请求通过同一个代理服务器…

【Redis】掌握篇--Redis与SSM进行整合

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Redis的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Redis与SSM的整合 1.添加Redis依赖 2…

网络安全应急响应工具(系统痕迹采集)-FireKylin

文章目录 网络安全应急响应工具(系统痕迹采集)-FireKylin1.FireKylin介绍【v1.4.0】 2021-12-20【v1.0.1】 2021-08-09 2.客户端界面Agent支持的操作系统FireKylinAgent界面使用方式比较传统方式与FireKylin比较无法可达目标的场景应用对比 3.使用教程设置语言Agent配置&#x…

掌握文件批量改名的技巧:实现跨文件夹文件统一命名及编号的实用方法“

在日常工作中&#xff0c;我们经常需要处理大量的文件&#xff0c;而这些文件的名字可能各不相同&#xff0c;给我们的管理工作带来了很大的不便。为了解决这个问题&#xff0c;今天我们为您推荐一款全新的文件批量改名工具&#xff0c;它可以帮助您在不同文件夹里的文件进行统…

操作系统复习(3)处理机调度与死锁

一、概述 1.1了解调度的层次 调度是指&#xff0c;在一个队列中&#xff0c;按照某种方法&#xff08;算法&#xff09;&#xff0c;选择一个合适的个体的过程。进程调度的功能就是按一定策略、动态地把CPU分配给处于就绪队列中的某一进程&#xff0c;并使之执行。 作业调度&…

【qemu逃逸】HWS2017-FastCP

前言 虚拟机用户名&#xff1a;root 虚拟机密码&#xff1a;无密码 本题有符号&#xff0c;所以对于设备定位啥的就不多说了&#xff0c;直接逆向设备吧。 设备逆向 在 realize 函数中设置一个时钟任务&#xff0c;并且可以看到只注册了 mmio&#xff0c;大小为 0x100000。…

小白必看!企业开源知识库管理系统优势和选择

在当今信息爆炸的时代&#xff0c;企业面临着海量的知识和信息需要管理和利用。为了提高团队的协作效率、促进知识共享和创新&#xff0c;越来越多的企业开始关注和采用开源企业知识库管理系统。这种系统为企业提供了一个集中化的平台&#xff0c;用于存储、组织和分享知识资产…

fio数据整理之二

fio数据简单抓取 上文我们完成了一些fio output数据的简单抓取&#xff0c;本文将针对抓取的数据做进一步的处理&#xff0c;输出到表格之中&#xff0c;方便我们查看&#xff0c;统计结果。 本文先使用最简单的方法创建csv档案 我们现有个基本认知&#xff0c;在csv档案中&am…

CBAM:Convolutional Block Attention Module

CBAM&#xff08;Convolutional Block Attention Module&#xff09;是一种深度学习领域的注意力机制&#xff0c;旨在增强卷积神经网络对图像特征的建模和表示能力。CBAM引入了通道和空间两种不同的注意力机制&#xff0c;使模型能够动态调整特征图的权重&#xff0c;以适应不…

MVC、MVP、MVVM区别

MVC、MVP、MVVM区别 MVC&#xff08;Model-View-Controller&#xff09; 。是一种设计模式&#xff0c;通常用于组织与应用程序的数据流。它通常包括三个组件&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Controller&…

sql中的加减乘除

自学SQL网(教程 视频 练习全套)

[vmware]vmware虚拟机压缩空间清理空间

vmware中的ubuntu使用如果拷贝文件进去在删除&#xff0c;vmare镜像文件并不会减少日积月累会不断是的真实物理磁盘空间大幅度减少&#xff0c;比如我以前windows操作系统本来只有30GB最后居然占道硬盘200GB&#xff0c;清理方法有2种。 第一种&#xff1a;vmware界面操作 第二…

阿里云服务器省钱购买和使用方法(图文详解)

阿里云服务器使用教程包括云服务器购买、云服务器配置选择、云服务器开通端口号、搭建网站所需Web环境、安装网站程序、域名解析到云服务器公网IP地址&#xff0c;最后网站上线全流程&#xff0c;新手站长xinshouzhanzhang.com分享阿里云服务器详细使用教程&#xff1a; 一&am…

数学建模比赛中常用的建模提示词(数模prompt)

以下为数学建模比赛中常用的建模提示词&#xff0c;希望对你有所帮助&#xff01; 帮我总结一下数学建模有哪些预测类算法&#xff1f; 灰色预测模型级比检验是什么意思? 描述一下BP神经网络算法的建模步骤 对于分类变量与分类变量相关性分析用什么算法 前10年的数据分别是1&a…

从传统货架到智能货架电子标签PTL仓储亮灯系统的革新

在现代物流仓储行业中&#xff0c;仓库的管理和物料的寻找一直是一个难题。仓库内物料数量种类繁多&#xff0c;寻找物料耗时长、困难大&#xff0c;盘点更是耗费人力多、成本高、速度慢。此外&#xff0c;货物存储位置不清晰&#xff0c;经常性找不到物料。多发、少发、错料现…

centos7安装oxidized备份软件

首先需要提前下载ruby&#xff0c;因为默认yum安装的版本太低 https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.0.tar.gz 1、yum remove ruby ruby-devel&#xff08;有就卸载&#xff0c;没有则忽略&#xff09; 2、将下载好的ruby包解压到/opt下 [rootoxidized ruby-…

【广州华锐互动】VR历史古城复原:沉浸式体验古代建筑,感受千年风华!

在科技日新月异的今天&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经成为了我们生活中不可或缺的一部分。从娱乐游戏到医疗健康&#xff0c;从教育培训到房地产销售&#xff0c;VR技术的应用领域日益广泛。而近年来&#xff0c;VR技术在文化遗产保护和古迹复原方面的…

阿里云安全恶意程序检测(速通一)

阿里云安全恶意程序检测 赛题理解赛题介绍赛题说明数据说明评测指标 赛题分析数据特征解题思路 数据探索数据特征类型数据分布箱型图 变量取值分布缺失值异常值分析训练集的tid特征标签分布测试集数据探索同上 数据集联合分析file_id分析API分析 特征工程与基线模型构造特征与特…

【stack题解】最小栈 | 栈的压入、弹出序列

目录 最小栈 思路 ​编辑 实现 栈的压入、弹出序列 思路 实现 需要注意的 最小栈 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。…