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编程技能,还让我们对音乐理论有了更深入的理解。通过合理的架构设计,我们可以构建出一个既实用又优雅的音乐工具。

相关推荐
kaliarch1 小时前
2025年IaC生态全景与实践指南:从工具选型到多云治理
后端·云计算·自动化运维
Coder-coco1 小时前
个人健康管理|基于springboot+vue+个人健康管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·mysql·论文
b***65321 小时前
springboot整合mybatis-plus(保姆教学) 及搭建项目
spring boot·后端·mybatis
5***E6851 小时前
Spring Boot与MyBatis
spring boot·后端·mybatis
x***01061 小时前
SpringSecurity+jwt实现权限认证功能
android·前端·后端
5***26221 小时前
Spring Boot问题总结
java·spring boot·后端
风生u2 小时前
go进阶语法
开发语言·后端·golang
666HZ6662 小时前
C语言——黑店
c语言·开发语言
Gomiko2 小时前
JavaScript基础(八):函数
开发语言·javascript·ecmascript