Kurzusok
/
Cairo
Cairo
/
Feladatok
/
Választás napja
Választás napja

Választás napja

Tanulófeladat

Bevezetés

Struktúrák

A struktúrák egyedi adattípusok, amelyek segítségével több kapcsolódó értéket nevezhetsz el és csomagolhatsz egybe.

Egy struktúra definiálásához használd a struct kulcsszót, utána egy nevet, majd kapcsos zárójeleket, amelyekben a mezők névvel és típussal szerepelnek:

#[derive(Drop)]
struct User {
    active: bool,
    username: ByteArray,
    email: ByteArray,
    sign_in_count: u64,
}

Egy struct példányának létrehozásához add meg a struct nevét, és használj kapcsos zárójeleket a mezők kulcs-érték párjaival.

let user = User {
   active: true, username: "someusername123", email: "someone@example.com", sign_in_count: 1
};

Pont jelöléssel érheted el vagy módosíthatod egy módosítható struct példány mezőit.

let mut user = User {
    active: true, username: "someusername123", email: "someone@example.com", sign_in_count: 1
};
user.email = "anotheremail@example.com";

Birtoklás

A Cairoban az értékek változtathatatlanok, és ez a változtathatatlanság biztosítja, hogy még ha sok változó ugyanarra az értékre is hivatkozik, maga az érték állandó és változatlan marad. Egy változó birtokosa az a kód, amely olvasni (és ha módosítható, írni) tudja azt a változót.

A birtoklás szabályai a Cairoban:

  1. A Cairoban minden változónak van birtokosa.
  2. Egyszerre csak egy birtokos lehet.
  3. Amikor a birtokos kikerül a scope-ból, a változó megsemmisül.

A scope a program azon tartománya, amelyben egy változó érvényes.

fn main() {
                      // `x` is not valid here, it has not been declared yet
    let x: u32 = 3;   // `x` is declared, it is valid from this point forward
                      // do stuff with `x`

} // this scope ended here, `x` goes out of scope and is no longer valid

Amikor egy változó kikerül a scope-ból, megsemmisül, ami biztosítja, hogy a hozzá tartozó erőforrás megfelelően felszabaduljon. Ez a Drop trait implementálásával vagy származtatásával érhető el, ami jelzi a fordítónak, hogy a típus biztonságosan megsemmisíthető, ha már nem hasznos.

#[derive(Drop)]
struct A {}

fn main() {
    A {}; // this value can be destroyed after going out of scope
}

Jelenleg a Drop implementáció minden típusra származtatható, kivéve a szótárakat (Felt252Dict) és a szótárakat tartalmazó típusokat.

Egy érték áthelyezhető is, ami akkor történik, amikor az értéket átadjuk egy másik függvénynek: megsemmisül az eredeti változó, amely az értékre hivatkozott (így a változó használhatatlanná válik), és létrejön egy új változó, amely ugyanazt az értéket tárolja.

fn foo(num: u32) { 
    // do something with p
}

fn main() {
    let num: u32 = 10;
    foo(num);
    // ownership of `num` was moved into `foo` and is thus no longer valid in this scope
}

Az értékek visszaadása egyenértékű az áthelyezésükkel.

Az alábbi példa egy olyan függvényt mutat, amely visszaad egy értéket:

#[derive(Drop)]
struct A {}

fn main() {
    // main now owns a1
    let a1 = gives_ownership();

    // a2 comes into scope
    let a2 = A {};

    // a2 is moved into takes_and_gives_back, which also moves its return value into a3
    let a3 = takes_and_gives_back(a2);
} // Here, a3 and a1 go out of scope and are dropped. a2 was moved, so nothing happens.

// Moves its return value into the function that calls it
fn gives_ownership() -> A {
    let some_a = A {};
    some_a
}

// This function takes an instance some_a of A and returns it
fn takes_and_gives_back(some_a: A) -> A {
    some_a
}

Mivel egy változó az áthelyezés után megsemmisül, a fordító megtiltja, hogy utána újra felhasználjuk.

#[derive(Drop)]
struct Point {
    x: u128,
    y: u128,
}

fn foo(p: Point) { 
    // do something with p
}

fn main() {
    let p = Point { x: 5, y: 10 };
    foo(p);
    foo(p); // compiler error: Variable was previously moved.
}

Ebben az esetben ugyanazt az értéket (a p változóban lévő Point struktúrát) próbáljuk átadni mindkét függvényhívásnak. Miután a változót először átadtuk a foo függvénynek, a változó már nem használható.

Egyik megoldás, ha származtatjuk a Copy traitet a struktúránkon, ami megváltoztatja, mi történik, amikor egy Copy változót adunk át egy függvénynek: már nem helyezné át az értéket, hanem létrehozna egy új változót, amely ugyanarra az értékre hivatkozik. Ez ingyenes művelet, mert a változók a Cairo absztrakciói, az értékek pedig mindig változtathatatlanok.

Ha implementálni szeretnéd a Copy traitet a típusodon, add hozzá a #[derive(Copy)] annotációt a típusdefinícióhoz.

#[derive(Copy, Drop)]
struct Point {
    x: u128,
    y: u128,
}

fn foo(p: Point) { 
    // do something with p
}

fn main() {
    let p = Point { x: 5, y: 10 };
    foo(p);
    foo(p); // this now works
}

Hivatkozások és pillanatképek

A Cairónak két funkciója van arra, hogy egy értéket megsemmisítés vagy áthelyezés nélkül adjon át: a references és a snapshots.

A pillanatképek egy változtathatatlan „nézetet” nyújtanak egy értékről egy adott pillanatban, lehetővé téve, hogy megtartsd egy változó birtoklását, miközben hozzáférsz az állapotához. A változó bármilyen módosítása új memóriacellát hoz létre, a pillanatkép nézete pedig változatlan marad.

Pillanatkép létrehozásához egyszerűen tedd a változópéldány elé a @ jelet.

// this function takes in a snapshot using `@` with the data-type
fn get_len(arr: @Array<u128>) -> u32 { // 
    arr.len()
}

fn foo(arr: Array<u128>) {
    arr.pop_front();
}

fn main() {
    let mut new_arr: Array<u128> = array![1, 2, 3, 4, 5, 6, 7, 8];
    let snapshot = @new_arr; // using `@` to create a snapshot of a variable
    let len = get_len(snapshot); // pass the snapshot as argument, retaining ownership
    foo(new_arr); // `new_arr` is still valid up until here and no further
}

Ha egy pillanatképet vissza szeretnél alakítani normál változóvá, használhatod a desnap operátort (*), amely a @ operátor ellentéte. Csak azok a típusok desnapelhetők, amelyek implementálják a Copy traitet.

fn square(n: @u32) -> u32 {
    let n = *n; // we desnap the value
    n * n
}

fn main() {
    let num: u32 = 10;
    let squared = square(@num); // we use `@` to create a snapshot
    println!("{} squared = {}", num, squared);
}

A hivatkozások lehetővé teszik, hogy módosítható értékeket adj át függvényeknek, és azok implicit módon visszaadódjanak, megőrizve a birtoklást a hívó kontextusában. Ez a ref módosítóval érhető el, feltéve, hogy a változó a mut kulcsszóval módosíthatóként van deklarálva.

Frissíthetjük a korábbi Point példát, hogy módosítható hivatkozásokat használjon a Copy trait helyett, és akkor is működni fog.

#[derive(Drop)]
struct Point {
    x: u128,
    y: u128,
}

fn foo(ref p: Point) { 
    // we can now update the passed values and the changes
    // will be visible in the calling context
    p.x += 1;
}

fn main() {
    let mut p = Point { x: 5, y: 10 };
    foo(ref p);
    foo(ref p); // this now works even without the Copy trait
    println!("p.x = {}", p.x); // p.x = 7 
}

Utasítások

A közeledben lévő helyi iskolának van egy nagyon aktív diákönkormányzata. A diákönkormányzatot egy elnök vezeti, és kétévente választásokat tartanak új elnök megválasztására. Az idei választáson úgy döntöttek, hogy új digitális rendszerre van szükség a szavazatok megszámlálásához.

Az iskola a segítségedet kéri az új rendszer megépítésében.

1. Szavazatszámláló létrehozása

Az új szavazási rendszer egyik első eleme egy szavazatszámláló. Ez a számláló arra való, hogy nyomon kövesse, hány szavazata van egy adott jelöltnek.

Hozz létre egy new_vote_counter függvényt, amely elfogadja egy jelölt kezdeti szavazatainak számát, és visszaadja egy VoteCounter struktúra pillanatképét, a megadott kezdeti szavazatszámmal inicializálva.

let initial_votes: u32 = 2;
let counter: @VoteCounter = new_vote_counter(initial_votes);
// @VoteCounter {
//     value: 2,
// }

2. Szavazatok számának lekérése egy számlálóból

Most már van módod új számlálók létrehozására! De most rájössz, hogy az új rendszernek is szüksége lesz egy módra, hogy lekérje a szavazatok számát egy számlálóból.

Hozz létre egy vote_count függvényt, amely argumentumként egy számlálót (@VoteCounter) fogad, és visszaadja a számlálóban lévő szavazatok számát.

let initial_votes: u32 = 3;
let vote_counter: @VoteCounter = new_vote_counter(initial_votes);

let count = vote_count(vote_counter);
// => 3

3. A számláló szavazatainak növelése

Végre eljött az idő a szavazatok megszámlálására! Most szükséged van egy módra, amellyel növelheted a szavazatok számát egy számlálóban.

Hozz létre egy increment_vote_count függvényt, amely argumentumként egy számlálót (@VoteCounter) és egy szavazatszámot fogad, majd megnöveli a számlálót ennyi szavazattal.

let votes: u32 = 3;
let vote_counter: @VoteCounter = new_vote_counter(initial_votes);
let vote_counter: VoteCounter = increment_vote_count(vote_counter, 2);

let count = vote_count(@vote_counter);
// => 5

4. A választási eredmények létrehozása

Most, hogy minden szavazatot megszámoltak, itt az idő előkészíteni az eredményhirdetést az egész iskola számára. Ehhez észreveszed, hogy nem elég, ha csak a szavazatok számlálói vannak. Kell egy mód arra, hogy a szavazatok számát egy adott jelölthöz rendeld.

Hozz létre egy new_election_result függvényt, amely megkapja egy jelölt nevét és a szavazatai számát, majd visszaad egy új választási eredményt.

let result: ElectionResult = new_election_result("Peter", 3);
// ElectionResult {
//     name: "Peter",
//     votes: @VoteCounter {
//         value: 3,
//     },
// }

A választási eredmény struktúra már létre van hozva neked, és így van definiálva:

struct ElectionResult {
    name: ByteArray,
    counter: @VoteCounter,
}

5. Az eredmények kihirdetése

Itt az idő bejelenteni az iskolának az új elnököt! Az új elnököt az iskola kis digitális üzenőtábláin fogják kihirdetni.

Az üzenetnek mutatnia kell az új elnök nevét és a kapott szavazatok számát, a következő formátumban: <candidate_name> (<votes>). Íme egy példa egy ilyen üzenetre: "Peter (51)".

Hozz létre egy display_result függvényt, amely argumentumként egy @ElectionResult-et fogad, és egy ByteArray-t ad vissza a megjelenítendő üzenettel.

let result = ElectionResult {
    name: "John",
    votes: @VoteCounter {
        value: 32,
    },
};

let msg = display_result(@result);
// => "John (32)"

6. Szavazatok újraszámlálása

Hogy megbizonyosodjanak a végeredmény pontosságáról, újraszámolták a szavazatokat. Az újraszámláláskor kiderült, hogy néhány jelölt szavazatainak száma eggyel eltért.

Hozz létre egy decrement_votes_of_candidate függvényt, amely megkapja a végeredményeket és egy jelölt nevét, akinek csökkentened kell a szavazatai számát. A végeredmények egy Felt252Dict<u32> formájában vannak megadva, ahol a kulcsok a jelöltek nevei, az értékek pedig az adott jelölt összes szavazata.

let mut final_results: Felt252Dict<u32> = Default::default();
final_results.insert('Mary', 10);
final_results.insert('John', 51);

decrement_votes_of_candidate(ref final_results, 'Mary');

let decremented_votes = final_results.get('Mary');
// => 9
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
Cairo Exercism

Készen állsz elkezdeni a(z) Választás napja feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Cairo nyelvet 25 fogalom68 feladat segítségével, valódi emberi mentorálással, mindez ingyen.