32 个发布版本
0.10.0 | 2022年11月3日 |
---|---|
0.9.1 | 2021年12月15日 |
0.8.1 | 2021年12月4日 |
0.7.0 | 2021年10月24日 |
0.2.6 | 2020年12月31日 |
#971 在 Rust 模式
64 每月下载量
76KB
1.5K SLoC
Advent of Code 辅助工具
本包包含一些我经常在我的 Advent of Code 解决方案中使用的辅助方法。
命令行参数处理
args::get_args
读取命令行参数并检查是否存在正确数量的参数。
示例
use rdcl_aoc_helpers::args::get_args;
fn example1() {
let args = get_args(&["<input file>", "<init>"], 1);
println!("{:?}", args);
}
fn example2() {
let args = get_args_repeating(&["<input file>", "<x1> ... <xn>"], 1);
println!("{:?}", args);
}
方向
Direction
枚举是一个方便的枚举,如果你正在处理基本方向(即上、下、左和右),它允许你转向和移动(使用 CanTravel
特性)。
错误处理
error::WithOrExit
此特性添加了一个 or_exit_with
方法。此方法的目的在于允许你轻松地让程序以特定的退出代码终止。它已针对 Result 和 Option 实现。针对 Result 的实现要求关联的错误类型实现 fmt::Debug。
示例
use rdcl_aoc_helpers::error::WithOrExit;
fn main() {
some_operation_that_returns_a_result()
.or_exit_with(25);
}
error::ParseError
包含仅消息的泛型错误。它实现了 fmt::Display 和 fmt::Debug,并且可以从 io::Error 和 num::ParseIntError 转换。
示例
use rdcl_aoc_helpers::parse_error;
use rdcl_aoc_helpers::error::ParseError;
fn example_with_params(param: u8) -> Result<(), ParseError> {
if process(param) {
Ok(())
} else {
Err(parse_error!("Failed to process param: {}", param))
}
}
fn example_without_params() -> Result<(), ParseError> {
if process() {
Ok(())
} else {
Err(parse_error!("Failed to process"))
}
}
I/O 操作
input::MultilineFromStr
此特性受 str::FromStr 特性的启发,并允许你解析可能跨越多行的输入。
示例
use rdcl_aoc_helpers::error::ParseError;
use rdcl_aoc_helpers::input::MultilineFromStr;
pub struct Record {
items: Vec<u8>,
}
impl MultilineFromStr for Record {
type Err = ParseError;
fn new() -> Self {
Record {
items: Vec::new(),
}
}
fn indicates_new_record(&self, line: &str) -> bool {
line.is_empty()
}
fn parse(&mut self, line: &str) -> Result<(), Self::Err> {
if !line.is_empty() {
self.items.push(line.parse::<u8>()?);
}
Ok(())
}
}
input::WithReadLines
这个特性添加了一个 read_lines
方法。该方法的目的是从某个源(例如文件)读取行,然后将每一行转换为特定类型。该方法接受一个退出码作为参数,在处理源失败时使用,并返回一个迭代器。此特性已针对 fs::File 实现。
示例
use rdcl_aoc_helpers::input::WithReadLines;
fn main() {
for item in File::open("./my-file.txt").read_lines::<u8>(1) {
println!("Item: {}", item);
}
}
input::WithReadMultiLines
这个特性添加了一个 read_multi_lines
方法。它等价于 input::WithReadLines
,但它依赖于 input::MultilineFromStr
而不是 str::FromStr。
示例
use rdcl_aoc_helpers::input::WithReadMultiLines;
fn main() {
for record in File::open("./my-file.txt").read_multi_lines::<Record>(1) {
println!("Item: {:?}", record);
}
}
#[derive(Debug)]
struct Record { /* ... */ }
impl MultilineFromStr for Record { /* ... */ }
input::WithAsRecords
& input::WithAsMultilineRecords
这些特性允许您轻松地将对象转换为所需类型的项的 vec。
示例
#[cfg(test)]
mod tests {
use rdcl_aoc_helpers::input::WithAsRecords;
use rdcl_aoc_helpers::input::WithAsMultilineRecords;
use super::*;
#[test]
fn test_simple() {
let input = vec!["1", "2", "3", "4", "5"]
.as_records::<u8>()
.unwrap();
assert_eq!(input, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_multiline() {
let input = vec!["1", "2", "", "3", "4", "", "5"]
.as_multiline_records::<Record>()
.unwrap();
assert_eq!(
input,
vec![
Record { items: vec![1, 2] },
Record { items: vec![3, 4] },
Record { items: vec![5] },
]
);
}
}
汇编机
此模块提供了一种可以处理类似汇编指令的机器。一个机器由三部分组成
- 指令(必须实现
MachineInstruction
) - 一个寄存器(必须实现
MachineRegister
) - 一个输出接收器(必须实现
OutputReceiver
)
当机器运行时,它将依次执行指令。每个指令指定下一个指令是什么(通常是下一行,但您也可以跳转到其他位置)。一旦程序计数器到达一个不存在的指令,机器将停止。指令可以使用寄存器来跟踪值,并可以使用输出接收器将输出发送到某些(假想)输出设备,例如 天线。在运行机器时,您还可以指定一个预执行钩子(PreExecuteHook
)。对于每个指令,机器将在实际执行之前调用此钩子。这允许您操纵程序的正常流程。例如
为了更容易地从输入文件中解析指令,此库提供了
machine::instruction::Value
- 表示原始值或寄存器的引用。machine::instruction::ParsedMachineInstruction
- 表示单个解析行。允许您轻松访问命令(.get_command()
)或特定参数,这些参数将被解析为正确的类型(.get_argument::<T>(idx)
)。
示例
use rdcl_aoc_helpers::error::ParseError;
use rdcl_aoc_helpers::machine::hook::NoopHook;
use rdcl_aoc_helpers::machine::instruction::{MachineInstruction, ParsedMachineInstruction, Value};
use rdcl_aoc_helpers::machine::output_receiver::OutputReceiver;
use rdcl_aoc_helpers::machine::register::MachineRegister;
use rdcl_aoc_helpers::machine::Machine;
fn main() {
let instructions = parse_input().unwrap();
let mut machine = Machine::new_simple_machine(&instructions);
machine.run(&mut NoopHook::default());
println!("Final register state: {}", machine.register);
}
fn parse_input() -> Result<Vec<Instruction>, ParseError> {
/* ... */
}
enum Instruction {
Add(Value, Value, char),
Jump(i64),
}
impl MachineInstruction for Instruction {
fn execute<R: MachineRegister, O: OutputReceiver<R>>(
&self,
register: &mut R,
_output_receiver: &mut O,
) -> i64 {
match self {
Instruction::Add(v1, v2, reg) => {
register.write(*reg, v1.get(register), v2.get(register));
1
}
Instruction::Jump(by) => by,
}
}
fn from_parsed_machine_instruction(
parsed: &ParsedMachineInstruction,
) -> Result<Self, ParseError> {
match parsed.get_command() {
"add" => Ok(Instruction::Add(
parsed.get_argument(0)?,
parsed.get_argument(1)?,
parsed.get_argument(2)?
)),
"jmp" => Ok(Instruction::Jump(
parsed.get_argument(0)?
)),
_ => Err(ParseError(format!("Unknown command: {}", parsed))),
}
}
}
impl FromStr for Instruction {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
<Self as MachineInstruction>::from_str(s)
}
}
数学
math::abs_diff
& math::taxi_cab_*d
abs_diff
函数计算两点之间的绝对差值。函数 taxi_cab_*d
(针对 2D、3D 和 4D 实现)计算两点之间的出租车距离。
示例
use rdcl_aoc_helpers::math::{abs_diff, taxi_cab_2d, taxi_cab_3d, taxi_cab_4d};
fn main() {
println!("|1 - 5| = {}", abs_diff(1, 5));
println!("|(1, 10) - (5, 3)| = {}", taxi_cab_2d((1, 10), (5, 3)));
println!("|(1, 10, 2) - (5, 3, 4)| = {}", taxi_cab_3d((1, 10, 2), (5, 3, 4)));
println!("|(1, 10, 2, 7) - (5, 3, 4, 2)| = {}", taxi_cab_4d((1, 10, 2, 7), (5, 3, 4, 2)));
}
math::gcd
计算两个数的最大公约数。
示例
use rdcl_aoc_helpers::math::gcd;
fn main() {
let a = 35;
let b = 49;
println!("gcd({}, {}) = {}", a, b, gcd(a, b));
}
math::lcm
计算两个数的最小公倍数。
示例
use rdcl_aoc_helpers::math::lcm;
fn main() {
let a = 35;
let b = 49;
println!("lcm({}, {}) = {}", a, b, lcm(a, b));
}
math::solve_crt
解(n1, a1)和(n2, a2)的中国剩余定理。我们假设
- n1 和 n2 互质
- n1 和 n2 不超过 63 位(因为它们被转换为 i64)
示例
use rdcl_aoc_helpers::math::solve_crt;
fn main() {
println!("solve_crt((3, 1), (5, 4)) = {}", solve_crt((3, 1), (5, 4)));
}
math::bezout_coefficients
找到t和s,使得ta + sb = gcd(p, q)。
示例
use rdcl_aoc_helpers::math::bezout_coefficients;
fn main() {
println!("bezout_coefficients(3, 4) = {}", bezout_coefficients(3, 4));
}
math::多项式::多项式
允许您处理多项式。目前支持
- 创建新的多项式(
let y = Polynomial::new(&[1, -25, 5])
)。 - 以人类可读的方式格式化多项式(
format!("{}", y)
)。 - 将两个多项式相加。
- 在点
x
处评估多项式(y.at(x)
)。 - 找到多项式的所有根(
y.find_roots()
)。
待办
- 实现更多算术运算(例如乘法)。
- 为多项式实现
Fn(i64) -> i64
,以便您可以调用它们(y(x)
,而不是y.at(x)
)。 - 要么实现
FromStr
,要么创建一个宏以更轻松地创建多项式(例如polynomial![x^2 - 25x + 5]
)。
示例
use rdcl_aoc_helpers::math::polynomial::Polynomial;
fn main() {
let y1 = Polynomial::new(&[1, -25, 5]);
println!("Solving {} = 0", y1);
for x in y1.find_roots() {
println!("For x = {}, y = {}.", x, y1.at(x));
}
let y2 = Polynomial::new(&[25, -5]);
println!("({}) + ({}) = {}", y1, y2, y1 + y2);
}
部分
部分::部分
如果需要显式引用一个部分,此枚举很有用。它实现了str::FromStr和fmt::Display,因此您可以轻松地将字符串转换为和从字符串转换。
示例
use rdcl_aoc_helpers::part::Part;
fn main() {
let part = "part 1".parse::<Part>().unwrap();
println!("[{}] ...", part); // outputs "[part 1] ..."
let part = Part::Two;
println!("[{}] ...", part); // outputs "[part 2] ..."
}
排列
排列::排列
生成给定集合的所有排列。
示例
use rdcl_aoc_helpers::permutations::Permutations;
fn main() {
for permutation in (0..5).collect::<Permutations<u8>>() {
println!("{:?}", permutation);
}
}
搜索
搜索::可导航
实现此特性,以便能够使用A*在两点之间找到最短路径。
依赖关系
~560KB
~11K SLoC