在学习一些生锈之前,让我们有一些背景,让我首先要学习生锈的动机。我使用不同的Java版本专业编写了Java,随着语言的增长,越来越多的Java版本功能旨在提高语言的表现力并降低现有语法的详细性,例如某些功能,例如记录,开关表达式或模式匹配。但是,我个人认为构建此类功能的动机受到其他语言(例如Rust或Scala)的影响。
由于记忆的安全性和性能,Rust是目前世界上最受欢迎和最受欢迎的语言之一。不仅如此,它还允许我编写与我在Java中所做的不同类型的编程类型,在该编程中,我主要编写OOP代码并且没有低级内存访问。那么,为什么不开始学习一些新的语言,例如Rust,在编写程序时(由于Rust没有执行任何编程范式),对记忆管理有更深入的了解,接触新的词汇和概念,并弄清楚为什么与Java相比,它更具性能?
在第一篇文章中,我们将从简单但非平凡的东西开始。结果应该是我们可以熟悉一些最重要的锈蚀语法,例如标准库,其一些数据结构,原始类型和前奏。一些词汇对我们来说可能是新的,但是在完成这篇文章后,我们将与它们相处。
让我们通过编写一个非常简单的解析器来开始在Rust的第一个编码挑战。我很高兴我的朋友提出了这个想法,而不是首先直接跳到更复杂的想法。这是我们需要做的:
给定一个文本文件,其中包含每行中的单词,在一个按字母顺序排序中输出所有唯一单词,并列出它们出现的所有行。如果其中任何一个出现在单行中多个,则只能为其打印一行编号。同样,每个单词出现的行编号也应以越来越多的顺序排序。
让我们素描一些关于完成此任务的高级想法:
阅读整个文本文件以获取内容
使用合适的数据结构方便地存储和处理输入
处理所有文件内容时输出结果
从阅读文件开始,我们介绍了Rust中模块的概念。这是组织和组相关功能的一种方式。例如, fs模块用于文件系统操纵功能,例如读取或编写文件,并且OS模块用于OS特定功能,例如文件权限和过程管理。要访问模块中的功能,我们可以使用::(双点),它们是用于浏览模块,结构,枚举和其他项目中的路径分离器。在这里,我们在std名称空间中存在fs模块。我们需要将fs模块导入到我们的代码中:
use std :: fs;fn read_file (file_path : & str ) - > std :: io :: Result < String > { fs :: read_to_string (file_path)}Rust中的命名约定是蛇案,这就是为什么我们拥有read_file和read_to_string原因。我们将文件路径作为参数并返回该文件的内容。在Rust中,函数的返回值由函数中的最后一个表达式指定,而没有半隆。 &str参数是代表对字符串切片的引用的类型。 &可以将其视为其他语言中的指针,但具有其他特征,例如借款(您可以在不负责其内存的情况下访问数据),默认情况下不可分割(不能修改引用的值(除非您明确使用可突出的参考( &mut )(&mut)( Ok(T) &mut), Result将无法返回的情况(返回的Err(E) )或返回的结果。如果我们的阅读操作成功。
前奏是自动导入每个RUST程序的常用类型,性状和功能的集合。类似于自动导入java.lang Java程序。
接下来,我们想遍历每一行,并缩小所有单词:
fn get_lines_lower_case (text : & str ) - > Vec < String > { text . to_lowercase () . lines () . map ( | line | line . to_string ()) . collect ()}我们看到一些新的语法|line| line.to_string()是封闭。这就是我们在Rust中创建匿名功能的方式。内部||将是函数参数,之后是功能主体,整个函数get_lines_lower_case将字符串列表返回为向量。
现在,让我们转到所需的文本的主要部分,然后返回一个数据结构,保留我们的排序输出:
fn process_text (text : & str ) - > BTreeMap < String , BTreeSet < usize > > { let mut word_lines : BTreeMap < String , BTreeSet < usize > > = BTreeMap :: new (); let lines = get_lines_lower_case (text);...}我们使用let关键字来声明Rust中的变量。 BTreeMap是类似于HashMap数据结构,但还包括另一个属性,其中其元素按密钥对其进行排序,键将是唯一的。这正是我们存储单词索引所需的。我们使用BTreeSet作为地图的价值,因为我们也希望唯一地存储单词出现的行,并希望将其分类。 usize是一种无符号的整数类型和平台依赖性,我使用MacOS,因此usize的大小将为64位(8个字节)。当我们想直接突变word_lines时,我们需要以mut关键字将其前缀。现在我们有一个完整的功能主体:
fn process_text (text : & str ) - > BTreeMap < String , BTreeSet < usize > > { let mut word_lines : BTreeMap < String , BTreeSet < usize > > = BTreeMap :: new (); let lines = get_lines_lower_case (text); for (line_number, line) in lines . iter () . enumerate () { let words : HashSet < String > = line . split_whitespace () . map ( | word | word . to_string ()) . collect (); for word in words { word_lines . entry (word) . or_insert_with ( BTreeSet :: new) . insert (line_number + 1 ); } } word_lines}在Rust中,元组是一个可以不同类型的多个值的集合。返回的lines.iter().enumerate()
对于每一行,我们将单词划分为空白,然后将它们存储在一组中。然后,我们迭代集合中的每个单词并更新word_lines 。首先,我们在地图中查找word ,如果找不到它,则entry函数将键入我们的地图中,还为此键创建一个新的BTreeSet值。 insert函数将在单词出现的一组线路中添加一个新的行号。我们需要向其添加1个,因为line_number以0开头。最后,最后一个word_lines返回我们的数据结构包装结果。
现在,我们创建另一个功能,该功能获取命令行参数并返回存储输入文件的路径:
fn read_file_from_args () - > io :: Result < String > { let args : Vec < String > = env :: args () . collect (); if args . len () < 2 { return Err ( io :: Error :: new ( io :: ErrorKind :: InvalidInput , "Usage: < file_path >" )); } let file_path = & args[ 1 ]; read_file (file_path)}args.len() > = 2将确保在尝试运行程序时至少有2个参数,因为第一个参数可以是通往货物可执行的路径,并且我们的输入路径将在第二个参数上,并且我们使用&args[1]访问它。
最后,我们现在准备将所有功能粘合在一起并执行程序:
use std :: collections :: { BTreeMap , BTreeSet , HashSet };use std :: env;use std :: fs;use std :: io;fn read_file (file_path : & str ) - > io :: Result < String > { fs :: read_to_string (file_path)}fn get_lines_lower_case (text : & str ) - > Vec < String > { text . to_lowercase () . lines () . map ( | line | line . to_string ()) . collect ()}fn process_words (content : & str ) - > BTreeMap < String , BTreeSet < usize > > { let mut word_map : BTreeMap < String , BTreeSet < usize > > = BTreeMap :: new (); let lines = get_lines_lower_case (content); for (line_number, line) in lines . iter () . enumerate () { let words : HashSet < & str > = line . split_whitespace () . collect (); for word in words { let word = word . to_string (); word_map . entry (word) . or_insert_with ( BTreeSet :: new) . insert (line_number + 1 ); } } word_map}fn read_file_from_args () - > io :: Result < String > { let args : Vec < String > = env :: args () . collect (); if args . len () < 2 { return Err ( io :: Error :: new ( io :: ErrorKind :: InvalidInput , "Usage: < file_path >" , )); } let file_path = & args[ 1 ]; read_file (file_path)}fn main () { let content = match read_file_from_args () { Ok (text) = > text, Err (e) = > { eprintln! ( "Error: {}" , e); std :: process :: exit ( 1 ); } }; let word_map = process_words ( & content); for (word, lines) in word_map { println! ( "Word: '{}', Lines: {:?}" , word, lines); }}类似于Maven或Java World的Gradle,Cargo是Rust的包裹经理,并在Rust中建造工具。要运行我们的程序,我们可以在项目中导航,并运行cargo命令:
cargo run -- / path / to / the / input / file . txt
我们有一个简单的输入文件:
Apple banana cherryBanana dog catRust is a programming languageHello world from RustThe quick brown fox jumpsThe lazy dog is cuteThis is a test fileFor testing random purposesBanana and apple are fruitsGoodbye and hello
我们期望看到的最终输出:
Word: 'a', Lines: {3, 7}Word: 'and', Lines: {9, 10}Word: 'apple', Lines: {1, 9}Word: 'are', Lines: {9}Word: 'banana', Lines: {1, 2, 9}Word: 'brown', Lines: {5}Word: 'cat', Lines: {2}Word: 'cherry', Lines: {1}Word: 'cute', Lines: {6}Word: 'dog', Lines: {2, 6}Word: 'file', Lines: {7}Word: 'for', Lines: {8}Word: 'fox', Lines: {5}Word: 'from', Lines: {4}Word: 'fruits', Lines: {9}Word: 'goodbye', Lines: {10}Word: 'hello', Lines: {4, 10}Word: 'is', Lines: {3, 6, 7}Word: 'jumps', Lines: {5}Word: 'language', Lines: {3}Word: 'lazy', Lines: {6}Word: 'programming', Lines: {3}Word: 'purposes', Lines: {8}Word: 'quick', Lines: {5}Word: 'random', Lines: {8}Word: 'rust', Lines: {3, 4}Word: 'test', Lines: {7}Word: 'testing', Lines: {8}Word: 'the', Lines: {5, 6}Word: 'this', Lines: {7}Word: 'world', Lines: {4}总之,这个简单的挑战教会了我们在Rust中的许多不同概念。希望您发现内容有用。另外,如果您对更多这样的帖子感兴趣,我们尝试通过某种编码挑战来学习新语言,请考虑订阅我的新闻通讯。我希望随着我们花更多的时间花在这种语言上,我们将倾向于需要更多基本的计算机科学知识的更复杂的问题。当时,我们可能会关注问题本身,而不是处理语言语法。愉快的编码,很快见!
[更新]该代码现在可以在Rust-coding-challenges上找到