Rust 中的函数式编程
由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!
一个月前,当我第一次使用ReasonML开发一个非商业项目时,我获得了一种全新的体验——函数式编程。这门语言是OCaml的另一种语法。OCaml 本身就是一种纯函数式语言,它提供的特性非常有趣。例如:类型推断、强类型系统、代数数据类型等等。是不是很有意思?
尝试之后,我对函数式编程产生了兴趣。最终,我尝试用另一种语言——Rust——来实现函数式编程范式。
介绍
函数式编程(FP)是一种编程范式,它允许我们编写富有表现力、简洁且优雅的代码。函数式编程还有助于开发者管理代码,避免重复编写相同的代码(DRY原则)。其他函数式语言包括Lisp、Clojure、Erlang、Haskell、R等等。
好吧,但是为什么是 Rust 呢?
问题是,Rust 是函数式语言吗?答案是否定的。虽然 Rust 本身受到了ML 系列语言的启发,但它并非函数式语言。不过幸运的是,Rust 拥有一些与其他函数式语言类似的特性,例如:代数数据类型、表达力强的类型等等。
目录
基本类型
为了避免贸然上手,我们最好先了解Rust中的几种数据类型。这一点也适用于所有编程语言。
布尔值
最基本的数据类型是简单true / false值,在 Rust 中称为bool
let x = true;
let y: bool = false;
查尔
该char数据类型只有一个 Unicode 值。我们可以使用char带单个反斜杠 ( ') 的数据类型。
let x = 'x';
let two_hearts = '💕';
与其他一些语言不同,charRust 中 不是一个字节,而是四个字节。
数字类型
Rust 有几种数值类型类别变体,例如 signed( i) 和 unsigned( u),固定大小(,,,8)和可变大小(,)类型。163264isizeusize
let x = 42; // `x` has type `i32`.
let y = 1.0; // `y` has type `f64`.
数组
像许多其他编程语言一样,Rust 也具有数组数据类型。默认情况下,Rust 中的数组无法更改,除非你使用 `new` 初始化它。mut
let a = [1, 2, 3]; // a: [i32; 3]
let mut m = [1, 2, 3]; // m: [i32; 3]
函数
函数也有数据类型!例如:
fn foo(x: i32) -> i32 { x }
let x: fn(i32) -> i32 = foo;
在这种情况下,该foo ()函数具有返回类型numeric: i32,并返回该值x。
更多信息,请查看这里:原始类型
关闭
闭包是一种机制,它允许内部函数在外部函数返回后,仍然能够访问外部函数词法作用域中定义的变量。理解到这里了吗?简而言之,闭包就是一个内部函数,它能够访问作用域内(包括内部和外部)的值。
fn fmt(prev_str: &str) -> String {
let mut new_str = String::new();
let closure_annotated = |next_str| -> String {
new_str.push_str(prev_str);
new_str.push_str(next_str);
return new_str;
};
closure_annotated("dolor sit amet")
}
let r_txt = "Lorem ipsum ";
assert_eq!("Lorem ipsum dolor sit amet", fmt(r_txt));
在这种情况下,在访问变量并更改其值的new_str.push_str ()部分。closure_annotatednew_str
咖喱
柯里化是函数式编程中的一种过程,它允许我们将一个带有多个参数的函数转换为一系列嵌套函数。柯里化会返回一个新函数,该函数期望下一个参数以内联方式传入。
#[derive(Debug)]
struct States<'a> {
a: &'a i32,
b: &'a i32,
}
trait Currying {
type ReturnType: Fn(i32) -> i32;
fn add(self) -> Self::ReturnType;
}
impl Currying for States<'static>{
type ReturnType = Box<dyn Fn(i32) -> i32>;
fn add(self) -> Self::ReturnType {
Box::new(move|x| {
x * self.a
})
}
}
let r_value: States = States {
a: &100,
b: &100
};
let r1 = r_value.add();
let r2 = r1(5);
assert_eq!(500, r2);
这里有两个参数,每个参数都有一个数值数据类型;然后,` asection`部分是一个函数接口,用于初始化函数。这些特性类似于TypeScript 接口。btrait
高阶函数(HOF)
高阶函数是指使用其他函数作为参数或返回值的函数。
fn map<F>(arr: &[i32], func: F) -> Vec<i32> where F: Fn(&i32) -> i32{
let mut new_array: Vec<i32> = vec![];
for i in arr.iter() {
new_array.push(func(i))
}
return new_array
}
let lists = vec![1, 4, 9, 16];
let result = map(&lists, |i| *i + 2);
assert_eq!(vec![3, 6, 11, 18], result)
所以,` funcand`map是高阶函数,其中此函数用于更改数组中的每个元素。返回结果是一个与修改后的数组长度相同的新数组originalArray。
懒惰评估
惰性求值或非严格求值是指在expression/function需要某个值时才对其进行求值。其目的是避免重复求值。
struct State {
x: i32,
}
trait Lazy {
fn add(&self) -> i32;
fn multiply(&self) -> i32;
fn add_or_multiply(&self, add: bool) -> i32;
}
impl Lazy for State {
fn add(&self) -> i32 {
println!("executing add");
&self.x + &self.x
}
fn multiply(&self) -> i32 {
println!("executing multiply");
&self.x * &self.x
}
fn add_or_multiply(&self, add: bool) -> i32 {
match add {
true => self.add(),
false => self.multiply(),
}
}
}
let val: State = State {
x: 20
};
assert_eq!(40, val.add_or_multiply(true));
assert_eq!(400, val.add_or_multiply(false));
函数式编程(FP)具有诸多优势,因此越来越受欢迎。然而,每种编程范式都有其独特的术语,函数式编程也不例外。我希望通过提供这份术语表,让学习函数式编程变得更加轻松✌️
源代码:rustfp.github
文章来源:https://dev.to/natserrat/function-programming-in-rust-3im8