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日

#971Rust 模式

Download history 4/week @ 2024-03-10 8/week @ 2024-03-31

64 每月下载量

MIT/Apache

76KB
1.5K SLoC

Advent of Code 辅助工具

Pipeline status

本包包含一些我经常在我的 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 方法。此方法的目的在于允许你轻松地让程序以特定的退出代码终止。它已针对 ResultOption 实现。针对 Result 的实现要求关联的错误类型实现 fmt::Debug

示例

use rdcl_aoc_helpers::error::WithOrExit;

fn main() {
    some_operation_that_returns_a_result()
        .or_exit_with(25);
}

error::ParseError

包含仅消息的泛型错误。它实现了 fmt::Displayfmt::Debug,并且可以从 io::Errornum::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::FromStrfmt::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