Məzmuna keç
Educora
Orta18 dəq7 / 10

Kolleksiyalar, qapanmalar və iteratorlar

`Vec` və `HashMap` ilə məlumat saxla, qapanmalar (closure) yaz və `map`, `filter`, `collect` kimi iterator metodları ilə dövrləri qısa və oxunaqlı zəncirlərə çevir.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • Vec-ə element əlavə etmək, silmək və onu üç üsulla gəzmək
  • HashMap ilə sayğac qurmaq və nəticəni sabit sırada çap etmək
  • Ətraf mühitdən dəyişən tutan qapanmalar yazmaq
  • map, filter, sum və collect ilə iterator zəncirləri qurmaq

Müəllim hər dərsdən sonra jurnala yeni qiymətlər əlavə edir — siyahı böyüyür. Sonra o, hansı qiymətin neçə dəfə yazıldığını saymaq istəyir — bu isə «açar → say» lüğətidir. Real proqramların əksəriyyəti məhz belə kolleksiyalarla işləyir. Rust-da bunun üçün Vec və HashMap, onları emal etmək üçün isə güclü iteratorlar var.

Vec — böyüyən siyahı

Vec<T> eyni tipli elementlərin heap-də saxlanılan, ölçüsü dəyişə bilən siyahısıdır. Onu vec![...] makrosu və ya Vec::new() ilə yaradırlar. push sona element əlavə edir, pop sonuncunu çıxarıb Option qaytarır, len isə uzunluğu verir. v[i] sərhəddən kənarda panikaya düşür, v.get(i) isə None qaytarır.

Rust
fn main() {
    let mut scores = vec![72, 95, 88];
    scores.push(64);
    println!("{:?} len={}", scores, scores.len());
    println!("first = {}", scores[0]);
    println!("get(10) = {:?}", scores.get(10));

    if let Some(last) = scores.pop() {
        println!("popped {last}");
    }
    for s in &mut scores {
        *s += 5;
    }
    println!("{:?}", scores);
}
Gözlənilən nəticə
[72, 95, 88, 64] len=4
first = 72
get(10) = None
popped 64
[77, 100, 93]
for s in &mut scores hər elementə dəyişən istinad verir; *s istinadın arxasındakı qiymətə müraciətdir.
DövrElementDövrdən sonra v
for x in &v&T — oxumaqistifadə oluna bilər
for x in &mut v&mut T — dəyişməkistifadə oluna bilər
for x in vT — sahiblik alınırötürülüb, istifadə olunmur

HashMap — açar və qiymət

HashMap<K, V> hər açara bir qiymət uyğunlaşdırır və onu std::collections-dan qoşmaq lazımdır. insert cüt əlavə edir, get isə Option<&V> qaytarır. Sayğac qurmağın ən qısa yolu entry API-dir: entry(açar).or_insert(0) açar yoxdursa, onu 0 ilə yaradır və qiymətə dəyişən istinad qaytarır.

Rust
use std::collections::HashMap;

fn main() {
    let text = "the cat and the dog and the bird";
    let mut counts: HashMap<&str, u32> = HashMap::new();
    for word in text.split_whitespace() {
        *counts.entry(word).or_insert(0) += 1;
    }
    println!("the: {:?}", counts.get("the"));
    println!("fish: {:?}", counts.get("fish"));

    let mut pairs: Vec<(&str, u32)> = counts.into_iter().collect();
    pairs.sort();
    println!("{:?}", pairs);
}
Gözlənilən nəticə
the: Some(3)
fish: None
[("and", 2), ("bird", 1), ("cat", 1), ("dog", 1), ("the", 3)]
split_whitespace() mətni boşluqlara görə sözlərə bölür. Nəticəni sabit sırada göstərmək üçün cütləri siyahıya yığıb çeşidlədik.

Qapanmalar (closures)

Qapanma (closure) yerindəcə yazılan adsız funksiyadır: |parametrlər| gövdə. Onun parametr tiplərini adətən yazmaq lazım olmur — kompilyator istifadədən çıxarır. Qapanmanın əsas xüsusiyyəti ətrafdakı dəyişənləri tutmasıdır: aşağıda add_bonus bonus-u oxuyur, click isə clicks-i dəyişir, ona görə özü də mut elan olunub.

Rust
fn main() {
    let bonus = 10;
    let add_bonus = |score: i32| score + bonus;
    let double = |x| x * 2;
    println!("{}", add_bonus(80));
    println!("{}", double(21));

    let mut clicks = 0;
    let mut click = || clicks += 1;
    click();
    click();
    println!("clicks = {clicks}");
}
Gözlənilən nəticə
90
42
clicks = 2

İteratorlar: map, filter, collect

v.iter() elementləri bir-bir verən iterator yaradır. map (hər elementi çevir) və filter (şərtə uyğunları saxla) kimi adapterlər tənbəldir: özləri heç nə hesablamır, yalnız plan qurur. İş collect, sum, count, max kimi yekun metod çağırılanda başlayır. collect-ə nəticənin tipini göstərmək lazımdır, məsələn, Vec<i32>.

Rust
fn main() {
    let numbers = vec![1, 2, 3, 4, 5, 6];

    let squares: Vec<i32> = numbers.iter().map(|n| n * n).collect();
    let evens: Vec<i32> = numbers.iter().copied().filter(|n| n % 2 == 0).collect();
    let total: i32 = numbers.iter().sum();
    let max = numbers.iter().max();

    println!("{:?}", squares);
    println!("{:?}", evens);
    println!("total = {total}, max = {max:?}");
    println!("{:?}", numbers);
}
Gözlənilən nəticə
[1, 4, 9, 16, 25, 36]
[2, 4, 6]
total = 21, max = Some(6)
[1, 2, 3, 4, 5, 6]
iter() elementləri borc götürür, ona görə numbers sonda dəyişməz qalır. copied() &i32 istinadlarını i32 qiymətlərinə çevirir. max boş siyahıda None qaytara bildiyi üçün nəticə Option-dur.
Dövr ilə
let names = vec!["aysel", "ali", "leyla", "rauf"];
let mut result = Vec::new();
for name in &names {
    if name.len() > 3 {
        result.push(name.to_uppercase());
    }
}
İterator zənciri ilə
let names = vec!["aysel", "ali", "leyla", "rauf"];
let result: Vec<String> = names
    .iter()
    .filter(|name| name.len() > 3)
    .map(|name| name.to_uppercase())
    .collect();
Hər iki variant ["AYSEL", "LEYLA", "RAUF"] verir. Zəncir yuxarıdan aşağı «nə edirik» kimi oxunur və result mut olmadan yaranır.
MetodNə edir
map(f)hər elementi f ilə çevirir
filter(p)p doğru olan elementləri saxlayır
enumerate()hər elementə indeks əlavə edir
zip(other)iki iteratoru cüt-cüt birləşdirir
take(n), skip(n)ilk n elementi götürür / buraxır
sum(), count(), max()cəm, say, ən böyük
any(p), all(p), find(p)biri uyğundurmu / hamısı uyğundurmu / birincini tap
collect()nəticəni kolleksiyaya yığır

Əsas fikirlər

  • Vec<T> böyüyən siyahıdır: push, pop, len; get(i) sərhəddən kənarda None qaytarır.
  • HashMap-də sayğac: *map.entry(k).or_insert(0) += 1; çap etməzdən əvvəl çeşidlə və ya BTreeMap işlət.
  • Qapanma |x| ifadə şəklindədir və ətrafdakı dəyişənləri tuta bilər.
  • map və filter tənbəldir; iş collect, sum, count kimi yekun metodla başlayır.
  • iter() borc götürür, into_iter() isə sahibliyi alır.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
Üç elementli Vec üçün v.get(10) nə qaytarır?