Rust 练习册 100:音乐音阶生成器

音乐和编程有着天然的联系,它们都需要严谨的逻辑和创造性思维。今天我们要探讨的是一个将音乐理论与编程实践相结合的问题:音阶生成器(Scale Generator)。通过这个练习,我们将学习如何用Rust实现一个能够生成各种音乐音阶的工具。

音乐理论基础

在深入代码之前,让我们先了解一些基本的音乐理论知识:

音名系统

在西方音乐中,有12个基本音符:

  • C, C#, D, D#, E, F, F#, G, G#, A, A#, B (使用升号 #)
  • C, Db, D, Eb, E, F, Gb, G, Ab, A, Bb, B (使用降号 b)

其中 # 表示升半音,b 表示降半音。C# 和 Db 是同一个音的不同表示方法。

音程

音程是两个音符之间的距离,以半音为单位:

  • m (小二度): 1个半音
  • M (大二度): 2个半音
  • A (增二度): 3个半音

音阶类型

  1. 半音阶(Chromatic Scale): 包含所有12个音符
  2. 大调(Major Scale): 音程模式为 MMmMMMm (2 2 1 2 2 2 1)
  3. 小调(Minor Scale): 音程模式为 MmMMmMM (2 1 2 2 1 2 2)
  4. 各种调式: 如多利亚调式(Dorian)、弗里吉亚调式(Phrygian)等

问题描述

我们的任务是实现一个音阶生成器,它需要提供以下功能:

rust 复制代码
// You should change this.
//
// Depending on your implementation, there are a variety of potential errors
// which might occur. They aren't checked by the test suite in order to
// allow the greatest freedom of implementation, but real libraries should
// provide useful, descriptive errors so that downstream code can react
// appropriately.
//
// One common idiom is to define an Error enum which wraps all potential
// errors. Another common idiom is to use a helper type such as failure::Error
// which does more or less the same thing but automatically.
#[derive(Debug)]
pub struct Error;

pub struct Scale;

impl Scale {
    pub fn new(tonic: &str, intervals: &str) -> Result<Scale, Error> {
        unimplemented!(
            "Construct a new scale with tonic {} and intervals {}",
            tonic,
            intervals
        )
    }

    pub fn chromatic(tonic: &str) -> Result<Scale, Error> {
        unimplemented!("Construct a new chromatic scale with tonic {}", tonic)
    }

    pub fn enumerate(&self) -> Vec<String> {
        unimplemented!()
    }
}

这个结构包含三个主要方法:

解决方案

下面是一个完整的实现:

rust 复制代码
#[derive(Debug, PartialEq)]
pub struct Error;

#[derive(Debug)]
pub struct Scale {
    notes: Vec<String>,
}

impl Scale {
    pub fn new(tonic: &str, intervals: &str) -> Result<Scale, Error> {
        // 定义升号和降号音阶的音符序列
        let sharps = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
        let flats = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
        
        // 根据主音决定使用升号还是降号
        let use_flats = matches!(tonic.to_lowercase().as_str(), 
            "f" | "bb" | "eb" | "ab" | "db" | "gb" | "d" | "g" | "c" | "f" | "bb" | "eb");
        
        let chromatic_scale = if use_flats { &flats } else { &sharps };
        
        // 找到主音在音阶中的位置
        let tonic_normalized = tonic.to_lowercase();
        let start_index = chromatic_scale.iter()
            .position(|&note| note.to_lowercase() == tonic_normalized)
            .ok_or(Error)?;
        
        let mut notes = vec![chromatic_scale[start_index].to_string()];
        let mut current_index = start_index;
        
        // 根据音程模式生成音阶
        for c in intervals.chars() {
            let step = match c {
                'm' => 1,  // 小二度
                'M' => 2,  // 大二度
                'A' => 3,  // 增二度
                _ => return Err(Error),
            };
            
            current_index = (current_index + step) % 12;
            notes.push(chromatic_scale[current_index].to_string());
        }
        
        Ok(Scale { notes })
    }

    pub fn chromatic(tonic: &str) -> Result<Scale, Error> {
        Scale::new(tonic, "mmmmmmmmmmmm")  // 12个半音步进
    }

    pub fn enumerate(&self) -> Vec<String> {
        self.notes.clone()
    }
}

测试案例详解

通过查看测试案例,我们可以更好地理解各种音阶的生成规则:

rust 复制代码
#[test]
/// Chromatic scale with sharps
fn test_chromatic_scale_with_sharps() {
    process_chromatic_case(
        "C",
        &[
            "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
        ],
    );
}

C半音阶使用升号系统,包含所有12个音符。

rust 复制代码
#[test]
/// Chromatic scale with flats
fn test_chromatic_scale_with_flats() {
    process_chromatic_case(
        "F",
        &[
            "F", "Gb", "G", "Ab", "A", "Bb", "B", "C", "Db", "D", "Eb", "E",
        ],
    );
}

F半音阶使用降号系统,这也是包含所有12个音符。

rust 复制代码
#[test]
/// Simple major scale
///
/// The simplest major scale, with no sharps or flats.
fn test_simple_major_scale() {
    process_interval_case("C", "MMmMMMm", &["C", "D", "E", "F", "G", "A", "B"]);
}

C大调音阶是最简单的,没有升降号,音程模式为MMmMMMm。

rust 复制代码
#[test]
/// Major scale with sharps
fn test_major_scale_with_sharps() {
    process_interval_case("G", "MMmMMMm", &["G", "A", "B", "C", "D", "E", "F#"]);
}

G大调音阶有一个升号F#。

rust 复制代码
#[test]
/// Minor scale with sharps
fn test_minor_scale_with_sharps() {
    process_interval_case("f#", "MmMMmMM", &["F#", "G#", "A", "B", "C#", "D", "E"]);
}

F#小调音阶使用升号系统。

rust 复制代码
#[test]
/// Pentatonic
fn test_pentatonic() {
    process_interval_case("A", "MMAMA", &["A", "B", "C#", "E", "F#"]);
}

五声音阶只有5个音符,模式为MMAMA。

设计思路解析

这个实现的关键点包括:

  1. 音符系统选择: 根据主音决定使用升号还是降号系统
  2. 音程解析: 将字符'm'、'M'、'A'转换为对应的半音步数
  3. 循环索引: 使用模运算处理音符的循环特性
  4. 错误处理: 当输入无效时返回错误

Rust语言特性运用

在这个实现中,我们运用了多种Rust语言特性:

  1. Result类型: 用于处理可能的错误情况
  2. 模式匹配: 使用[matches!]宏简化条件判断
  3. 迭代器: 使用[position]方法查找元素位置
  4. 字符串处理: 处理大小写转换和字符串比较
  5. 生命周期: 正确处理引用参数

实际应用场景

音阶生成器在很多场景下都非常有用:

  1. 音乐软件开发: 作曲软件、音序器等
  2. 教育工具: 音乐理论学习应用
  3. 游戏开发: 音乐游戏、节奏游戏
  4. 音频处理: 合成器、音效处理器
  5. 人工智能音乐: 算法作曲系统

总结

通过这个练习,我们学习到了:

  1. 如何将音乐理论知识转化为程序实现
  2. 复杂问题的模块化设计方法
  3. 错误处理在实际项目中的重要性
  4. Rust在处理字符串和集合类型方面的优势
  5. 音乐和编程之间的有趣联系

这个练习不仅帮助我们提高Rust编程技能,还让我们对音乐理论有了更深入的理解。通过合理的架构设计,我们可以构建出一个既实用又优雅的音乐工具。

相关推荐
AAA阿giao2 小时前
qoder-cli:下一代命令行 AI 编程代理——全面解析与深度实践指南
开发语言·前端·人工智能·ai编程·mcp·context7·qoder-cli
m0_748250032 小时前
C++ 修饰符类型
开发语言·c++
李日灐2 小时前
C++STL:仿函数、模板(进阶) 详解!!:“伪装术”和模板特化、偏特化的深度玩法指南
开发语言·c++·后端·stl
rgeshfgreh2 小时前
Python连接KingbaseES数据库全指南
开发语言·数据库·python
Wang's Blog2 小时前
Nodejs-HardCore: 玩转 EventEmitter 指南
开发语言·nodejs
何中应2 小时前
使用Spring自带的缓存注解维护数据一致性
java·数据库·spring boot·后端·spring·缓存
DYS_房东的猫2 小时前
《 C++ 零基础入门教程》第5章:智能指针与 RAII —— 让内存管理自动化
开发语言·c++·自动化
零度@2 小时前
Java 消息中间件 - 云原生多租户:Pulsar 保姆级全解2026
java·开发语言·云原生
jghhh012 小时前
基于MATLAB的分块压缩感知程序实现与解析
开发语言·算法·matlab
一路向北⁢2 小时前
企业级敏感词拦截检查系统设计方案(Spring Boot)
spring boot·后端·bootstrap·敏感词·敏感词拦截