Monday, 5 December 2016

[Rust] Học Rust để tính toán

Việc học không có mục đích sẽ khó đi đến đích, dễ lạc lối và hoang mang.

Bài này giới thiệu đủ các khái niệm của Rust để tính toán cơ bản, cụ thể là làm vài bài ProjectEuler.net
Nếu ai hỏi học Rust làm gì, có ai thuê đâu? thì câu trả lời là mình học cho đỡ vui 🤑


Các khái niệm cần thiết gồm có:
- Đặt biến để chứa giá trị
- Thực hiện các phép toán cơ bản: cộng trừ nhân chia, chia lấy dư
- Các câu lệnh điều khiển: if/else if/else
- Các vòng lặp: while, for
- In ra màn hình: println!

Tạo project bằng câu lệnh:
cargo new --bin rustmath
Bấm vào đây để xem thêm về cách tạo Rust project và dùng cargo.

Sửa file rustmath/src/main.rs
Đoạn code sau để kiểm tra số x có phải số chẵn không, in ra màn hình.
fn main() {
    let x = 42;
    if x % 2 == 0 {
        println!("{} is even", x);
    } else {
        println!("{} is odd", x);
    }
}
Giải thích:
- Dùng hai dấu bằng ``==`` để kiểm tra xem giá trị vế trái và vế phải có bằng nhau không. Ở đây kiểm tra x chia cho 2 có dư 0 hay không (có chia hết không).
- Để in ra màn hình, dùng "macro": println!  - có dấu ! chỉ rằng đây là một macro, macro là gì tạm thời để sau, giờ cứ biết làm thế để in ra màn hình.
- Trong khi gọi macro!(), dùng dấu ``{}`` để giữ chỗ cho giá trị sẽ được thay thế sau đó. Nội dung in ra nằm trong dấu nháy kép (double quotes): "{} is odd".

Thử làm bài Project Euler 1:
Tính tổng các số nhỏ hơn 1000 chia hết cho 3 hoặc chia hết cho 5.

fn main() {
    let mut sum = 0;
    for i in 1..1000 {
        if i % 3 == 0 {
            sum += i;
        } else if  i % 5 == 0 {
            sum += i;
        }
    }
    println!("{}", sum);
}
Giải thích:
- Các ngôn ngữ khác thường gọi là "biến" - variable, Python gọi là "name", Rust gọi là "binding".
- mut sum: mut là viết tắt của "mutable", tức ở đây ta muốn "binding" ``sum`` có giá trị thay đổi được (để tăng dần và chứa tổng của các số). Mặc định một binding sẽ là "immutable", tức không thay đổi được về mặt giá trị sau khi đã khởi tạo giá trị ban đầu.
let x = 5;
x = 6; // error!
let mut y = 5;
y = 6; // OK 
- for: dùng khi cần lặp một số lần nhất định. Với cú pháp: for binding in N..M
Giá trị của M không được tính, vì vậy muốn lặp từ 1 đến 999 thì viết for i in 1..1000
- Rust dùng if/else if/else để điều khiển các nhánh rẽ, giống như if/elif/else trong Python.

Thử làm thêm bài Project Euler  2 nữa:
tính tổng các số fibonacci chẵn <= 4 triệu.

Bài này ta cần:
- 1 mutable binding chứa tổng,
- 1 mutable binding chứa số Fibonacci thứ nhất,
- 1 mutable binding chứa số Fibonacci thứ hai,
- 1 mutable binding làm biến tạm để chứa giá trị trung gian
- Lặp không xác định lần, biết điều kiện dừng lại: số Fibonacci lớn đạt đến giá trị 4.000.000
fn main() {
    let mut sum = 0;
    let mut fib1 = 0;
    let mut fib2 = 1;
    let mut temp;
    while fib2 < 4000000 {
        if fib2 % 2 == 0 {
            sum += fib2
        }
        temp = fib1;
        fib1 = fib2;
        fib2 = temp + fib2;
    }
    println!("{}", sum);
}
Có thể dùng ``loop`` để lặp vô hạn lần kết hợp với ``break`` để thoái khỏi 1 vòng lặp đang bao nó.
fn main() {
    let mut sum = 0;
    let mut fib1 = 0;
    let mut fib2 = 1;
    let mut temp;

    loop {
        if fib2 >= 4000000 { break; }
        if fib2 % 2 == 0 {
            sum += fib2
        }
        temp = fib1;
        fib1 = fib2;
        fib2 = temp + fib2;
    }
    println!("{}", sum);
}
Còn đây là lời giải bài toán lớp 3 nổi danh:
Dùng cách thô thiển nhất, vẫn ra kết quả như mong đợi trong khoảng 16s:
fn main() {
    let mut counter = 0;
    for a in 1..10 {
        for b in 1..10 {
            for c in 1..10 {
                for d in 1..10 {
                    for e in 1..10 {
                        for f in 1..10 {
                            for g in 1..10 {
                                for h in 1..10 {
                                    for i in 1..10 {
                                        if 13 * b * i == c * ((87+f-a-d-e*12)*i - g*h) {
                                            counter += 1
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    println!("{}", counter)
}

Từng ấy Rust là đủ dùng để làm toán phổ thống, ProjectEuler.
Bài tiếp theo sẽ giới thiệu các khái niệm khác của Rust kèm ứng dụng của chúng.

Hết.
HVN at http://familug.org and http://pymi.vn