Rust 程序設計語言學習——函數式語言功能:迭代器和閉包

Rust 的閉包(closures)是可以保存在一個變量中或作為參數傳遞給其他函數的匿名函數。可以在一個地方創建閉包,然后在不同的上下文中執行閉包運算。不同于函數,閉包允許捕獲被定義時所在作用域中的值。

迭代器(iterator)負責遍歷序列中的每一項和決定序列何時結束的邏輯。當使用迭代器時,我們無需重新實現這些邏輯。

一、閉包

閉包,一個可以儲存在變量里的類似函數的結構。

1.1 閉包會捕獲其環境

在 Rust 中,閉包是一種匿名函數,它們可以捕獲外部環境中的變量。閉包的捕獲行為取決于變量的類型和閉包如何使用這些變量。

通過值捕獲(By Value)

當閉包通過值捕獲一個變量時,它獲取了該變量的所有權。這意味著在閉包被創建之后,原始變量不能再被使用。

fn main() {let text = "Hello".to_string();// 使用 move 來顯式地表示閉包將獲取 text 的所有權let closure = move || println!("{}", text);// 這里 text 不能被使用,因為其所有權已經被閉包獲取// println!("{}", text); // 這將導致編譯錯誤closure(); // 打印 "Hello"
}

通過引用捕獲(By Reference)

當閉包通過引用捕獲一個變量時,它借用了該變量。這意味著原始變量仍然可用,但閉包只能借用它,不能獲取所有權。

fn main() {let text = "Hello";// 閉包通過引用捕獲 textlet closure = || println!("{}", text);// text 仍然可用,因為它沒有被移動println!("{}", text); // 打印 "Hello"closure(); // 再次打印 "Hello"
}

可變捕獲(Mutable Capture)

閉包可以捕獲一個可變引用,允許它修改原始變量的值。

fn main() {let mut count = 0;// 閉包通過可變引用捕獲 countlet mut closure = || {count += 1; // 修改 count 的值println!("Count: {}", count);};closure(); // 打印 "Count: 1"closure(); // 打印 "Count: 2"// count 的值現在是 2println!("Final count: {}", count);
}

如果移除閉包的 mut 修飾,編譯不通過。

fn main() {let mut count = 0;// 閉包通過可變引用捕獲 countlet closure = || {count += 1; // 修改 count 的值println!("Count: {}", count);};closure(); // 打印 "Count: 1"closure(); // 打印 "Count: 2"// count 的值現在是 2println!("Final count: {}", count);
}

報錯信息提示:由于借用了可變的 count,調用 closure 需要可變的綁定。

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `closure` as mutable, as it is not declared as mutable--> src/main.rs:4:9|
4 |     let closure = || {|         ^^^^^^^ not mutable
5 |         count += 1; // 修改 count 的值|         ----- calling `closure` requires mutable binding due to mutable borrow of `count`
...
8 |     closure(); // 打印 "Count: 1"|     ------- cannot borrow as mutable
9 |     closure(); // 打印 "Count: 2"|     ------- cannot borrow as mutable|
help: consider changing this to be mutable|
4 |     let mut closure = || {|         +++For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` (bin "playground") due to 1 previous error

閉包作為參數

閉包可以作為參數傳遞給其他函數,捕獲環境中的變量。

fn main() {// 創建一個整數變量let number = 10;// 創建一個閉包,它接受一個 i32 類型的參數并返回其平方// 這里使用 || 表示這是一個閉包let square = || number * number;// 定義一個函數,它接受一個閉包作為參數并調用它// 閉包作為參數需要指定其類型,這里使用 || -> i32 表示閉包沒有參數并返回 i32 類型的值fn call_closure<F>(f: F)whereF: Fn() -> i32, // 使用 trait bound 指定閉包的簽名{// 調用閉包并打印結果let result = f();println!("The result is: {}", result);}// 調用 `call_closure` 函數,并將閉包 `square` 作為參數傳遞// 由于閉包 `square` 沒有參數,我們可以直接傳遞call_closure(square);
}

在這個示例中:

  1. 我們定義了一個閉包 square,它通過引用捕獲了 main 函數中的 number 變量,并計算其平方。
  2. 我們定義了一個名為 call_closure 的函數,它接受一個符合 Fn() -> i32 trait bound 的閉包作為參數,這意味著閉包沒有參數并返回一個 i32 類型的值。
  3. 我們調用 call_closure 函數,并將 square 閉包作為參數傳遞。由于 square 閉包沒有參數,它可以直接作為參數傳遞給 call_closure
  4. call_closure 函數調用閉包并打印出結果。

1.2 閉包類型推斷和注解

函數與閉包還有更多區別。閉包并不總是要求像 fn 函數那樣在參數和返回值上注明類型。函數中需要類型注解是因為它們是暴露給用戶的顯式接口的一部分。嚴格定義這些接口對保證所有人都對函數使用和返回值的類型理解一致是很重要的。與此相比,閉包并不用于這樣暴露在外的接口:它們儲存在變量中并被使用,不用命名它們或暴露給庫的用戶調用。

閉包通常很短,并只關聯于小范圍的上下文而非任意情境。在這些有限制的上下文中,編譯器能可靠地推斷參數和返回值的類型,類似于它是如何能夠推斷大部分變量的類型一樣(同時也有編譯器需要閉包類型注解的罕見情況)。

類似于變量,如果我們希望增加明確性和清晰度也可以添加類型標注,壞處是使代碼變得更啰嗦(相對于嚴格必要的代碼)。

fn main() {let a = 100;let add_one = |x: i32| -> i32 { x + 1 };let b = add_one(a);println!("{}", b); 
}

有了類型注解閉包的語法就更類似函數了。如下是一個對其參數加一的函數的定義與擁有相同行為閉包語法的縱向對比。這里增加了一些空格來對齊相應部分。這展示了除了使用豎線以及一些可選語法外,閉包語法與函數語法有多么地相似:

fn  add_one_v1   (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x|             { x + 1 };
let add_one_v4 = |x|               x + 1  ;

所以上例可以簡化為:

fn main() {let a = 100;let add_one = |x| x + 1;let b = add_one(a);println!("{}", b); 
}

編譯器會為閉包定義中的每個參數和返回值推斷一個具體類型。

再來看一個示例:

fn main() {let a = 100i32;let a1 = 100f32;let closure = |x| x;let b = closure(a);let b1 = closure(a1);println!("{}", b); println!("{}", b1); 
}

注意這個閉包定義沒有增加任何類型注解,所以我們可以用任意類型來調用這個閉包。但是如果嘗試調用閉包兩次,第一次使用 i32,第二次使用 f32 就會報錯:預期的,因為之前使用“i32”類型的參數調用了閉包。

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types--> src/main.rs:6:22|
6 |     let b1 = closure(a1);|              ------- ^^ expected `i32`, found `f32`|              ||              arguments to this function are incorrect|
note: expected because the closure was earlier called with an argument of type `i32`--> src/main.rs:5:21|
5 |     let b = closure(a);|             ------- ^ expected because this argument is of type `i32`|             ||             in this closure call
note: closure parameter defined here--> src/main.rs:4:20|
4 |     let closure = |x| x;|                    ^For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 1 previous error

1.3 將被捕獲的值移出閉包和 Fn trait

一旦閉包捕獲了定義它的環境中一個值的引用或者所有權(也就影響了什么會被移進閉包,如有),閉包體中的代碼定義了稍后在閉包計算時對引用或值如何操作(也就影響了什么會被移出閉包,如有)。閉包體可以做以下任何事:將一個捕獲的值移出閉包,修改捕獲的值,既不移動也不修改值,或者一開始就不從環境中捕獲值。

閉包捕獲和處理環境中的值的方式影響閉包實現的 trait。Trait 是函數和結構體指定它們能用的閉包的類型的方式。取決于閉包體如何處理值,閉包自動、漸進地實現一個、兩個或三個 Fn trait。

  1. FnOnce 適用于能被調用一次的閉包,所有閉包都至少實現了這個 trait,因為所有閉包都能被調用。一個會將捕獲的值移出閉包體的閉包只實現 FnOnce trait,這是因為它只能被調用一次。
  2. FnMut 適用于不會將捕獲的值移出閉包體的閉包,但它可能會修改被捕獲的值。這類閉包可以被調用多次。
  3. Fn 適用于既不將被捕獲的值移出閉包體也不修改被捕獲的值的閉包,當然也包括不從環境中捕獲值的閉包。這類閉包可以被調用多次而不改變它們的環境,這在會多次并發調用閉包的場景中十分重要。

下面是一個示例,展示如何在函數中使用 FnOnce 作為泛型約束,并且確保閉包只被調用一次:

fn call_once<F, T>(f: F) -> T
whereF: FnOnce() -> T, // 約束 F 為 FnOnce trait,意味著它接受一個空參數并返回 T 類型
{f() // 調用閉包并返回結果
}fn main() {// 創建一個閉包,它捕獲了 `value` 的所有權let value = 42;let consume = move || {let result = value; // 移動 `value`println!("The value is: {}", result);result // 返回結果};// 調用 `call_once` 函數,傳入閉包let result = call_once(consume);println!("Result of the closure: {}", result);// 嘗試再次使用 `consume` 將會導致編譯錯誤,因為它已經消耗了 `value`// call_once(consume);
}

運行結果

The value is: 42
Result of the closure: 42

在這個示例中,我們定義了一個泛型函數 call_once,它接受一個類型為 F 的參數 f,其中 F 必須實現 FnOnce trait。這意味著 f 是一個閉包,它接受空參數并返回類型 T 的結果。

main 函數中,我們創建了一個閉包 consume,它捕獲了 value 的所有權。然后,我們調用 call_once 函數,傳入 consume 閉包。call_once 函數調用閉包并返回其結果。由于 consume 閉包已經消耗了 value,嘗試再次調用 call_once 傳入相同的閉包將會導致編譯錯誤。

下面是一個使用 FnMut trait 的示例。

fn apply_mut<F, T>(func: &mut F, num: i32) -> T
whereF: FnMut(i32) -> T, // F 是一個可變閉包,接受一個 i32 類型的參數并返回類型為 T 的結果
{func(num) // 調用閉包并返回結果
}fn main() {let mut count = 0;// 創建一個閉包,它接受一個 i32 類型的參數并將其加到 count 上let mut increment = |num: i32| -> i32 {count += num;count};// 使用 apply_mut 函數和 increment 閉包的引用let result: i32 = apply_mut(&mut increment, 5);println!("Result after applying increment: {}", result);// 再次使用 apply_mut 函數和 increment 閉包的引用let result: i32 = apply_mut(&mut increment, 10);println!("Result after applying increment again: {}", result);
}

運行結果

Result after applying increment: 5
Result after applying increment again: 15

在這個例子中,apply_mut 函數接受一個 F 類型的可變引用 &mut F 作為參數(這樣,increment 閉包就不會被移動,可以被多次使用)和一個 i32 類型的參數 numwhere 子句指定了 F 必須是一個實現了 FnMut 的閉包,它接受一個 i32 類型的參數并返回一個類型為 T 的結果。main 函數中創建了一個可變閉包 increment,它修改了一個捕獲的變量 count,然后使用 apply_mut 函數來調用這個閉包。每次調用 apply_mut 都會使用 increment 閉包,并且 increment 閉包都會修改 count 的值。

在 Rust 中,Fn trait 表示閉包不會獲取它捕獲變量的所有權,也不會改變這些變量。下面的例子展示了如何定義一個接受 Fn 閉包作為參數的函數,并在并發場景中使用它。

use std::thread;// 定義一個函數,它接受一個實現了 Fn(i32) -> i32 的閉包,并調用它
fn call_once<F, T>(func: F) -> T
whereF: Fn(i32) -> T, // 指定 F 是一個接受 i32 并返回 T 的 Fn 閉包
{let result = func(42); // 調用閉包,傳入一個 i32 類型的值result // 返回閉包的執行結果
}fn main() {// 定義一個簡單的 Fn 閉包,它接受一個 i32 類型的參數并返回兩倍的該值let double = |x: i32| -> i32 {x * 2};// 創建多個線程,每個線程都使用相同的閉包let handles: Vec<_> = (0..5).map(|i| {let func = double; // 閉包可以被復制,因為它是 Fn 類型的thread::spawn(move || {let result = call_once(func); // 調用 call_once 函數,并傳入閉包println!("Thread {} result: {}", i, result); // 打印結果})}).collect();// 等待所有線程完成for handle in handles {handle.join().unwrap();}
}

不是每次運行都是這個順序,具體哪個線程先運行,要看系統的本次調度。

運行結果

Thread 1 result: 84
Thread 2 result: 84
Thread 0 result: 84
Thread 4 result: 84
Thread 3 result: 84

在這個例子中:

  1. 我們定義了一個 call_once 函數,它接受一個泛型參數 F,并且 F 必須滿足 Fn(i32) -> T 的 trait bounds。這意味著 F 是一個閉包,它接受一個 i32 類型的參數并返回一個類型為 T 的結果。
  2. main 函數中,我們定義了一個簡單的閉包 double,它接受一個 i32 類型的參數 x 并返回 x * 2 的結果。
  3. 我們使用 map 創建了 5 個線程,每個線程都復制了 double 閉包,并在新線程中調用 call_once 函數,將閉包作為參數傳遞給 call_once
  4. 在每個線程中,call_once 函數被調用,執行閉包,并打印出結果。
  5. 最后,我們使用 join 方法等待所有線程完成。

二、迭代器

迭代器允許你對一個序列的項進行某些處理。接下來主要介紹使用迭代器處理元素序列和循環 VS 迭代器性能比較。在 Rust 中,迭代器是惰性的(lazy),這意味著在調用消費迭代器的方法之前不會執行任何操作。

2.1 Iterator trait 和 next 方法

迭代器都實現了一個叫做 Iterator 的定義于標準庫的 trait。這個 trait 的定義看起來像這樣:

pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;// 此處省略了方法的默認實現
}

type ItemSelf::Item,它們定義了 trait 的關聯類型(associated type)。不過現在只需知道這段代碼表明實現 Iterator trait 要求同時定義一個 Item 類型,這個 Item 類型被用作 next 方法的返回值類型。換句話說,Item 類型將是迭代器返回元素的類型。

nextIterator 實現者被要求定義的唯一方法。next 一次返回迭代器中的一個項,封裝在 Some 中,當迭代器結束時,它返回 None

2.2 消費迭代器的方法

Iterator trait 有一系列不同的由標準庫提供默認實現的方法;你可以在 Iterator trait 的標準庫 API 文檔中找到所有這些方法。一些方法在其定義中調用了 next 方法,這也就是為什么在實現 Iterator trait 時要求實現 next 方法的原因。

這些調用 next 方法的方法被稱為消費適配器(consuming adaptors),因為調用它們會消耗迭代器。

fn main() {let numbers = vec![1, 2, 3, 4, 5];// 使用into_iter()將Vec轉換為消費迭代器let numbers_iter = numbers.iter();let mut sum: i32 = numbers_iter// 使用sum適配器計算迭代器中所有元素的總和.sum();// 打印總和println!("The sum is: {}", sum);
}

如果再去使用 numbers_iter 就會報錯。 sum 方法獲取迭代器的所有權并反復調用 next 來遍歷迭代器,因而會消費迭代器。當其遍歷每一個項時,它將每一個項加總到一個總和并在迭代完成時返回總和。調用 sum 之后不再允許使用 numbers_iter,因為調用 sum 時它會獲取迭代器的所有權。

2.3 產生其他迭代器的方法

Iterator trait 中定義了另一類方法,被稱為迭代器適配器(iterator adaptors),它們允許我們將當前迭代器變為不同類型的迭代器。可以鏈式調用多個迭代器適配器。不過因為所有的迭代器都是惰性的,必須調用一個消費適配器方法以便獲取迭代器適配器調用的結果。

fn main() {let v1: Vec<i32> = vec![1, 2, 3];let v2: Vec<_> = v1.iter().map(|x| x * x).collect();assert_eq!(v2, vec![1, 4, 9]);
}

collect 方法消費迭代器并將結果收集到一個數據結構中。因為 map 獲取一個閉包,可以指定任何希望在遍歷的每個元素上執行的操作。

2.4 使用捕獲其環境的閉包

很多迭代器適配器接受閉包作為參數,而通常指定為迭代器適配器參數的閉包會是捕獲其環境的閉包。

fn main() {let numbers = vec![1, 2, 3, 4, 5];// 使用into_iter()將Vec轉換為消費迭代器let filtered_and_squared: Vec<i32> = numbers.into_iter()// 使用filter適配器過濾元素,接受一個閉包作為參數// 閉包捕獲其環境,這里指numbers的元素.filter(|&x| x % 2 == 0) // 保留偶數// 使用map適配器對過濾后的元素進行變換,也接受一個閉包.map(|x| x * x) // 對每個元素進行平方// 使用collect適配器將結果收集到一個新的Vec中.collect();// 打印過濾和平方后的結果println!("The filtered and squared numbers are: {:?}", filtered_and_squared);
}

運行結果

The filtered and squared numbers are: [4, 16]

在這個例子中,filtermap 都是迭代器適配器,它們接受閉包作為參數。這些閉包可以捕獲其環境,即迭代器中的元素。在 filter 適配器中,閉包 |&x| x % 2 == 0 用于檢查元素是否為偶數,如果是,則元素會被保留。在 map 適配器中,閉包 |x| x * x 用于對每個元素進行平方操作。由于這些適配器都是消費性的,它們會消耗原始的迭代器,因此不能再次使用。最后,collect 適配器將處理后的元素收集到一個新的 Vec 中。

2.5 循環 VS 迭代器性能比較

迭代器,作為一個高級的抽象,被編譯成了與手寫的底層代碼大體一致性能的代碼。迭代器是 Rust 的零成本抽象(zero-cost abstractions)之一,它意味著抽象并不會引入運行時開銷。

fn main() {let numbers1 = (0..1000000).collect::<Vec<i64>>();let numbers2 = (0..1000000).collect::<Vec<i64>>();let mut sum1 = 0i64;let mut sum2 = 0i64;// 測量for循環的性能let start = std::time::Instant::now();for val in numbers1 {sum1 += val;}let loop_duration = start.elapsed();// 測量迭代器的性能let start = std::time::Instant::now();for val in numbers2.iter() {sum2 += val;}let iterator_duration = start.elapsed();println!("Iterator took: {:?}", iterator_duration);println!("For loop took: {:?}", loop_duration);
}

運行結果

Iterator took: 12.796012ms
For loop took: 11.559512msIterator took: 12.817732ms
For loop took: 11.687655msIterator took: 12.75484ms
For loop took: 11.89468msIterator took: 12.812022ms
For loop took: 11.785106msIterator took: 12.78293ms
For loop took: 11.528941ms

從這個例子可以看出迭代器還是稍微慢一些。

參考鏈接

  1. Rust 官方網站:https://www.rust-lang.org/zh-CN
  2. Rust 官方文檔:https://doc.rust-lang.org/
  3. Rust Play:https://play.rust-lang.org/
  4. 《Rust 程序設計語言》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/42379.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/42379.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/42379.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++ STL 隨機數用法介紹

目錄 一:C語言中的隨機數 二:C++中的隨機數 1. 生成隨機數的例子 2. 隨機數引擎 3. 隨機數引擎適配器 4. C++中預定義的隨機數引擎,引擎適配器 5. 隨機數分布 一:C語言中的隨機數 <stdlib.h>//初始化隨機種子 srand(static_cast<unsigned int>(time(nullptr)…

C#面: 依賴注入有哪幾種方式?

依賴注入&#xff08;Dependency Injection&#xff0c;簡稱DI&#xff09;是一種設計模式&#xff0c;用于解耦組件之間的依賴關系。在C#中&#xff0c;常見的依賴注入方式有以下幾種&#xff1a; 構造函數注入&#xff08;Constructor Injection&#xff09;&#xff1a;通過…

dependencyManagement的作用、nacos的學習

使用SpringCloudAlibaba注意各組件的版本適配 SpringCloudAlibaba已經包含了適配的各組件&#xff08;nacos、MQ等&#xff09;的版本號&#xff0c;也是一個版本仲裁者&#xff0c;但是可能已經有了父項目Spring-Boot-Starter-Parent這個版本仲裁者&#xff0c;又不能加多個父…

什么是獨立服務器?

獨立服務器是指一個單獨的物理服務器&#xff0c;整體的硬件設施都是獨立存在的&#xff0c;有著強大的性能&#xff0c;只需要運行用戶個人的數據信息&#xff0c;并且可以享受到獨立服務器的硬件與軟件&#xff0c;當網站有著大量的用戶進行訪問或者是需要運行大型的軟件時&a…

leetcode熱題100.零錢兌換(動態規劃)

今天給大家分享一道動態規劃的常考題&#xff0c;零錢兌換&#xff0c;很有趣的動態規劃題目&#xff0c;希望可以對大家找工作過程中起到幫助&#xff0c;幫助大家拓展下思維 給你一個整數數組 coins &#xff0c;表示不同面額的硬幣&#xff1b;以及一個整數 amount &#xf…

6、Redis系統-數據結構-06-跳表

六、跳表&#xff08;Skiplist&#xff09; 跳表是一種高效的動態數據結構&#xff0c;可以用于實現有序集合&#xff08;Sorted Set&#xff0c;Zset&#xff09;。與平衡樹相比&#xff0c;跳表具有實現簡單、效率高的優點&#xff0c;因此被 Redis 選用作為有序集合的底層數…

階段三:項目開發---搭建項目前后端系統基礎架構:任務13:實現基本的登錄功能

任務描述 任務名稱&#xff1a; 實現基本的登錄功能 知識點&#xff1a; 了解前端Vue項目的基本執行過程 重 點&#xff1a; 構建項目的基本登陸功能 內 容&#xff1a; 通過實現項目的基本登錄功能&#xff0c;來了解前端Vue項目的基本執行過程&#xff0c;并完成基…

如何讓代碼兼容 Python 2 和 Python 3?Future 庫助你一臂之力

目錄 01Future 是什么? 為什么選擇 Future? 安裝與配置 02Future 的基本用法 1、兼容 print 函數 2、兼容整數除法 3、兼容 Unicode 字符串 03Future 的高級功能 1. 處理字符串與字節 2. 統一異常處理…

linux kthread任務管理

目錄 一、linux 創建內核線程1.1 kthread_create1.2 kthread_create_worker kthread_queue_work 二、設置線程優先級和調度策略2.1 sched_setscheduler2.2 調度策略 一、linux 創建內核線程 1.1 kthread_create 在 linux 中&#xff0c;可以使用 kthread_create 接口創建內核…

移動校園(7)ii:uniapp路由響應攔截器處理token,以及微信小程序報錯當前頁面正在處于跳轉狀態,請稍后再進行跳轉....

依據昨天的寫完&#xff0c;在token過期之后&#xff0c;再次調用接口&#xff0c;會觸發后端攔截&#xff0c;扔進全局錯誤處理中間件 前端說明提示都沒有&#xff0c;只有一個這個&#xff0c;現在優化一下&#xff0c;再寫一個類似全局后置守衛&#xff0c;當狀態碼是401的時…

MySQL——數據連接池

數據庫連接 --- 執行完畢 --- 釋放&#xff08;連接到釋放的過程十分浪費系統資源&#xff09; 池化技術&#xff1a;準備一些預先的資源&#xff0c;過來就連接預先準備好的 編寫連接池&#xff0c;實現一個接口 DataSource 開源數據源實現&#xff08;拿來即用&#xff09;…

增強安全防護,解讀智慧校園系統的登錄日志功能

在構建智慧校園系統時&#xff0c;登錄日志功能扮演著不可或缺的角色&#xff0c;它不僅是系統安全的守護者&#xff0c;也是提升管理效率和確保合規性的有力工具。這一機制詳細記錄每次登錄嘗試的方方面面&#xff0c;涵蓋了時間戳、用戶身份、登錄來源的IP地址乃至使用的設備…

phpcms 升級php8.3.8

windows 2008 server 不支持php8.3.8,需升級為windows 2012 1.下載php8.3.8 PHP8.3.9 For Windows: Binaries and sources Releases 2.配置php.ini (1.)在php目錄下找到php.ini-development文件&#xff0c;把它復制一份&#xff0c;改名為php.ini (2.)修改php安裝目錄 根…

《昇思 25 天學習打卡營第 10 天 | ResNet50 遷移學習 》

《昇思 25 天學習打卡營第 10 天 | ResNet50 遷移學習 》 活動地址&#xff1a;https://xihe.mindspore.cn/events/mindspore-training-camp 簽名&#xff1a;Sam9029 使用遷移學習進行狼狗圖像分類 簡介 在機器學習和深度學習中&#xff0c;我們經常面臨數據不足的問題。 遷…

python【文件操作】

文件操作 一、創建文件夾二、文件操作模式1.覆蓋寫入2.讀取3.追加 三、 Python腳本在文件中查找和替換文本四、 python清空文件夾 一、創建文件夾 判斷文件或者文件夾是否存在 import ospathrD://測試文件夾 if not os.path.exists(path):os.mkdir(path)print(os.path.exists…

C++模板元編程(二)——完美轉發

完美轉發指的是函數模板可以將自己的參數“完美”地轉發給內部調用的其它函數。所謂完美&#xff0c;即不僅能準確地轉發參數的值&#xff0c;還能保證被轉發參數的左、右值屬性不變。 文章目錄 場景舊的方法新的方法內部實現參考文獻 場景 思考下面的代碼&#xff1a; templ…

高防服務器的重要性

在數字化時代&#xff0c;網絡安全已成為企業和個人最為關注的問題之一。隨著網絡攻擊的日益頻繁和復雜&#xff0c;傳統的服務器租用服務已難以滿足高安全需求的市場。高防服務器租用應運而生&#xff0c;成為保護網絡安全的重要解決方案。本文將探討高防服務器租用的概念、重…

專業140+總分420+天津大學815信號與系統考研經驗天大電子信息與通信工程,真題,大綱,參考書。

順利上岸天津大學&#xff0c;專業課815信號與系統140&#xff0c;總分420&#xff0c;總結一些自己的復習經歷&#xff0c;希望對于報考天大的同學有些許幫助&#xff0c;少走彎路&#xff0c;順利上岸。專業課&#xff1a; 815信號與系統&#xff1a;指定教材吳大正&#xf…

2-26 基于matlab開發的制冷循環模型

基于matlab開發的制冷循環模型。Simscape兩相流域中的制冷循環模型&#xff0c;在simulink中完成多循環溫度控制。程序已調通&#xff0c;可直接運行。 2-26 制冷循環模型 Simscape兩相流域 - 小紅書 (xiaohongshu.com)

Arduino ESP8266 開發環境搭建

Arduino ESP8266 開發環境搭建 很久之前學嵌入式時&#xff0c;用過Arduino8266進行開發&#xff0c;開發成本低、難度小&#xff0c;體驗很不錯。 近期&#xff0c;又突然要用&#xff0c;遂再次搭建環境&#xff0c;但變動挺多&#xff0c;有些小波折&#xff0c;開貼記錄。…