首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >锈蚀中的素筛(Eratosthenes筛)

锈蚀中的素筛(Eratosthenes筛)
EN

Code Review用户
提问于 2017-03-20 09:48:24
回答 1查看 1K关注 0票数 6

我正在学习Rust,我决定制作一个主要的筛子程序。它的工作和性能与我的同类C程序相当。

我是生锈的新手,所以我想要一些关于我的代码的反馈。特别是,我的代码是否采用了正确的锈蚀样式?

我知道有一些改变使算法更快,但我更喜欢反馈锈蚀代码的样式。

代码语言:javascript
复制
use std::env;
use std::fs::File;
use std::io::{Write, BufWriter};


fn prime_sieve(n: usize, ofile: &String) -> u32 {
    let f = File::create(ofile).expect("Unable to create file");
    let mut f = BufWriter::new(f);

    let mut prime_mask: Vec<bool> = vec![true;n];
    prime_mask[0] = false;
    prime_mask[1] = false;
    let mut p = 2;  // First prime number

    let mut count = 0;  // Total number of primes found
    let mut i;

    // Main sieve loop
    while p<n {
        if prime_mask[p] {
            f.write_all(format!("{}\n",p).as_bytes())
                .expect("Unable to write to file");
            count+=1;
            i=2*p;
            while i<n {
                prime_mask[i] = false;
                i += p;
            }
        }
        p+=1;
    }
    count
}


fn main() {
    let args: Vec<String> = env::args().collect();
    let n: usize = args[1]
        .trim()
        .parse()
        .expect("Wanted a number");
    let ofile = &args[2];
    let np = prime_sieve(n,ofile);
    println!("Found {} primes less than {}. Wrote to {}", np, n, &args[2]);
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2017-03-20 13:36:08

  1. 学会爱铁锈
    1. 空格围绕运算符- count+=1;+ count += 1;- i=2*p;+i=2* p;- while i
    2. 函数参数之间的空间--让np = prime_sieve(n,ofile),+让np = prime_sieve(n,ofile);
    3. 空格位于数组和;声明中的Vec之后。- vec;+ vec;

  2. 不要拿&String,拿一个&str相反,
  3. 根本不需要指定prime_mask的类型,推断可以处理它。
  4. 让您的代码讲述故事,而不是评论。引入具有名称的常量并重命名现有变量。
  5. "N“通常是指某物的计数。对于素数,我希望N的意思是“返回我的第一个N素数”。在这种情况下,它是“检查第一个N个数字,看看它们是否是素数”。一个更好的名字将有助于传达这一点。
  6. 不要声明范围比所需范围更广的变量。可以在初始化的位置声明i
  7. 每个迭代都添加一个循环,可以用基于范围的for循环替换。请注意,这消除了代码中的可更改性。
  8. 使用write!而不是分配一个String并编写它。
  9. 使用writeln!而不是自己指定换行符,
  10. 不需要指定args向量的内部类型。使用_可以让编译器推断它。
  11. 不要将“文件”的概念硬编码到prime_sieve函数中,而是接受任何实现Write的类型。让测试变得更容易。
代码语言:javascript
复制
use std::env;
use std::fs::File;
use std::io::{Write, BufWriter};

fn prime_sieve<W>(max_number_to_check: usize, mut output: W) -> u32
    where W: Write
{
    let mut prime_mask = vec![true; max_number_to_check];
    prime_mask[0] = false;
    prime_mask[1] = false;

    let mut total_primes_found = 0;

    const FIRST_PRIME_NUMBER: usize = 2;
    for p in FIRST_PRIME_NUMBER..max_number_to_check {
        if prime_mask[p] {
            writeln!(output, "{}", p).expect("Unable to write to file");
            total_primes_found += 1;
            let mut i = 2 * p;
            while i < max_number_to_check {
                prime_mask[i] = false;
                i += p;
            }
        }
    }

    total_primes_found
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let n: usize = args[1].trim().parse().expect("Wanted a number");

    let ofile = &args[2];
    let f = File::create(ofile).expect("Unable to create file");
    let f = BufWriter::new(f);

    let np = prime_sieve(n, f);

    println!("Found {} primes less than {}. Wrote to {}", np, n, ofile);
}

对于您的使用,Vec<bool>会浪费空间。每个bool是一个字节,您使用的是一个字节。相反,有高效的位向量箱将数据打包得更紧密。

在将自文档添加到程序中时显式地放入类型难道没有好处吗?我来自Haskell,它还推断了类型,但鼓励显式类型签名。

在Rust中需要显式类型签名;不能推断函数的参数或返回类型。铁锈是从Haskell这里学到的,并且避免了如果你不提供签名的话,在很远的地方会发生惊悚行为的问题。

函数内部的推理是非常惯用的。有时您需要显式(例如集合类型到collect ),但除此之外,我不愿列出任何类型。

我有一种感觉,随着你习惯于锈蚀,这是一种改变。初学者更可能分散类型,然后学习信任和依赖推理。

如果存在歧义,那么改进变量名可能会有所帮助。例如,对我来说,“掩码”是一个整数,它使用按位运算对另一个整数应用。我不希望它是一个Vec

我还使用了高度动态的语言,比如Ruby,在那里没有静态类型,所以函数中缺少类型并不过分。

对于Vec<bool>类型,它们总是浪费空间吗?是否存在使用它们而不是位-vec类型的情况?

每个数据结构都有权衡。我知道的主要问题是,位向量不允许您获得对特定布尔值的引用,因为布尔值实际上并不存在。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/158274

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档