CSCI 1200 - 作业 1 - Spotify播放列表
作业要求
Details
请先阅读并理解学术诚信政策,然后再开始这份家庭作业。
在这次作业中,你需要开发一个像 Spotify 那样管理音乐播放列表的程序,���们称之为 New York Playlists。请先完整地阅读整个手稿再开始编写代码。
学习目标
- 熟悉处理命令行参数。
- 熟悉文件输入和输出操作。
- 练习使用 C++ 标准模板库中的 string 和 vector 类。
命令行参数
你的程序将这样运行:
|
|
这里:
- nyplaylists.exe 是可执行文件名。
- playlist.txt 是包含播放列表的输入文件,我们将在本 README 中称其为 playlist file(播放列表文件)。
- actions.txt 是定义一系列操作的输入文件,在本 README 中我们将它称为 actions file(动作文件)。
- output.txt 输出结果的位置。
播放列表文件格式和输出文件格式
播放列表文件和输出文件具有相同的格式。以 playlist_tiny1.txt 为例,该文件有以下四行:
|
|
除了第二行外,每一行有两个字段:音乐标题和艺术家。这两个字段之间有一个空格分隔。
第二行是特殊的,它以单词 current 结尾,表示歌曲 “Always Remember Us This Way” 正在播放。这个单词 current 在 playlist file 中只出现一次,并且也应在输出文件中出现一次。
动作文件格式
动作文件定义了操作。以 actions1.txt 为例,该文件有以下几行:
|
|
actions file 可能包括五种不同类型的动作:
- add,将歌曲添加到播放列表的末尾。
- remove,从播放列表中移除一首歌。
- move,移动一首歌到新的位置 - 新的位置总是在行的最后。例如,move “I Will Never Love Again - Film Version” Lady Gaga, Bradley Cooper 1 将歌曲 “I Will Never Love Again - Film Version” 移动到位置 1,而 move “You Are Still the One” Shania Twain 4 将歌曲 “You Are Still the One” 移动到位置 4。注意,不同于 C/C++ 中的数组索引,Spotify 的定位从 1 开始,而不是从 0 开始。
- next,跳过正在播放的歌曲并开始播放列表中紧随其后的歌曲。如果当前播放的歌曲已经在播放列表底部,则操作 next 将使第一首歌(即,位于顶部的歌曲)成为当前播放的歌曲。
- previous,跳过正在播放的歌曲并回到列表中的前一首歌。如果当前播放的歌曲已在顶部,则操作 previous 将使最后一首歌(即,在底部的歌曲)成为当前播放的歌曲。
根据这个样本 actions file,将有四首歌被添加到播放列表中,有一首歌会被移除,两首歌会被移动,并且正在播放的歌曲将会是不同的歌曲,而不是 “Always Remember Us This Way” 这一首歌。
当 playlist_tiny1.txt 和 actions1.txt 作为两个输入文件提供给你的程序时,你的程序应该生成以下输出文件:
|
|
不存在的歌曲
如果在 actions file 中定义的动作试图移动或移除播放列表中不存在的歌曲,你的程序应该忽略这样的动作。
重复的歌曲
当同一首歌出现在播放列表中的多个位置时,请选择第一首(要移动或删除) - 即从顶部到底部搜索播放列表,找到该歌曲的第一个出现的位置,并使用它进行操作。
教师代码
你可以在这里测试(但不能查看)教师的代码:instructor code。请注意,此站点托管在 RPI 的网络上,你只能通过 RPI 的网络访问该站点:要么在校内,要么使用 VPN 服务。此外,请注意,在本作业中不需要播放音乐,教师的 C++ 代码仅用于管理播放列表。
程序要求及提交详情
在这次作业中,你需要同时使用 std::string 和 std::vector。你不允许使用我们尚未学习过的任何数据结构。
在设计和实现程序时,请采用良好的编码风格。将你的程序组织成函数:不要把所有代码都放在 main 函数里!请务必阅读 家庭作业政策 以完善你的解决方案。确保为你的程序编写新的测试用例,完全调试它,并不要忘记注释代码!完成提供的模板 README.txt。你必须独自完成这项作业,如 合作政策及学术诚信 页面所述。如果你与任何人讨论了问题或错误消息等,请在 README.txt 文件中列出他们的名字。按照课程网页上的指示准备并提交你的作业。如果需要帮助,可以向助教咨询。
截止日期: 2025年1月16日,晚上10点。
打分标准
13 分
- README.txt 完成 (3 分)
- 名字、合作者或小时数未填写的 (-1)
- 名字、合作者或小时数中有两个或以上未填写的 (-2)
- 没有反思 (-1)
- STL Vector & String (3 分)
- 使用了本课程尚未覆盖的数据结构 (-3)
- 没有用到 STL vector (-2)
- 没有用到 STL string (-2)
- 程序结构 (7 分)
- 显著不完整实现,无分 (-7)
- 几乎所有代码都在 main 函数中。建议为不同的任务创建单独的函数。(-2)
- 不当使用或遗漏 const 和引用 (-1)
- 几乎没有有用的注释 (-4)
- 注释太少 (-2)
- 包含无用的注释,如被注释掉的代码、终端命令或愚蠢的笔记 (-1)
- 排版过于拥挤、空格过多或缩进不当 (-1)
- 缺少错误检查(参数数量、无效文件名、无效命令等)(-1)
- 变量命名不佳:非描述性名称(例如 ‘vec’,‘str’,‘var’),单字母变量名(除了循环计数器)等 (-2)
- 使用全局变量 (-1)
- 行过长,超过大约 100 字符。建议保持所有行短,并将注释放在单独的行上 (-1)
支持文件
spotify_playlists.7z程序设计
在开始之前,我们需要找出需要做什么。让我们画一个流程图来检查步骤。
flowchart TB A(("Start")) --> D["读取 'playlist file' 和 'actions file'" ] subgraph "初始化" D --> E["查找当前歌曲的索引(如果有)"] end E --> F{"对于 'actions file' 中的每个动作"} subgraph "处理动作" F -- next --> G["查找当前歌曲的索引(如果有)"] G --> H["从当前歌曲中移除 'current' 标记"] H --> I{"是否是最后一首歌?"} I -- Yes --> J["设置索引为 0"] I -- No --> K["设置索引为 index+1"] J --> L["标记新的当前歌曲"] K --> L["标记新的当前歌曲"] F -- previous --> M["查找当前歌曲的索引(如果有)"] M --> N["从当前歌曲中移除 'current' 标记"] N --> O{"是否是第一首歌?"} O -- Yes --> P["设置索引为最后��首歌"] O -- No --> Q["设置索引为 index-1"] P --> R["标记新的当前歌曲"] Q --> R["标记新的当前歌曲"] F -- add --> S["构建新歌曲字符串"] S --> T["添加到播放列表"] F -- remove --> U["构建要移除的歌曲字符串"] U --> V["查找第一个出现的位置(如果有)"] V --> W["从播放列表中移除(如果未找到则忽略)"] F -- move --> X["构建要移动的歌曲字符串"] X --> Y["检查 'move' 目标位置"] Y --> Z["查找第一个出现的位置(如果有)"] Z --> ZA["从播放列表中移除(如果未找到则忽略)"] ZA --> ZB["在新位置插入"] endflowchart TB A(("Start")) --> D["读取 'playlist file' 和 'actions file'" ] subgraph "初始化" D --> E["查找当前歌曲的索引(如果有)"] end E --> F{"对于 'actions file' 中的每个动作"} subgraph "处理动作" F -- next --> G["查找当前歌曲的索引(如果有)"] G --> H["从当前歌曲中移除 'current' 标记"] H --> I{"是否是最后一首歌?"} I -- Yes --> J["设置索引为 0"] I -- No --> K["设置索引为 index+1"] J --> L["标记新的当前歌曲"] K --> L["标记新的当前歌曲"] F -- previous --> M["查找当前歌曲的索引(如果有)"] M --> N["从当前歌曲中移除 'current' 标记"] N --> O{"是否是第一首歌?"} O -- Yes --> P["设置索引为最后��首歌"] O -- No --> Q["设置索引为 index-1"] P --> R["标记新的当前歌曲"] Q --> R["标记新的当前歌曲"] F -- add --> S["构建新歌曲字符串"] S --> T["添加到播放列表"] F -- remove --> U["构建要移除的歌曲字符串"] U --> V["查找第一个出现的位置(如果有)"] V --> W["从播放列表中移除(如果未找到则忽略)"] F -- move --> X["构建要移动的歌曲字符串"] X --> Y["检查 'move' 目标位置"] Y --> Z["查找第一个出现的位置(如果有)"] Z --> ZA["从播放列表中移除(如果未找到则忽略)"] ZA --> ZB["在新位置插入"] end
然后,我们可以计划在这个程序中使用哪些函数。
flowchart TB subgraph "Main" main["main()"] end subgraph "文件 I/O" load_list("load_list()") get_text("get_text()") write_list("write_list()") end subgraph "辅助函数" is_all_digits("is_all_digits()") tokenizer("tokenizer()") check_in_list("check_in_list()") remove_in_list("remove_in_list()") get_current("get_current()") build_song("build_song()") end %% 连接 main --> load_list load_list --> get_text main --> write_list main --> is_all_digits main --> tokenizer main --> check_in_list main --> remove_in_list main --> get_current main --> build_song remove_in_list --> check_in_listflowchart TB subgraph "Main" main["main()"] end subgraph "文件 I/O" load_list("load_list()") get_text("get_text()") write_list("write_list()") end subgraph "辅助函数" is_all_digits("is_all_digits()") tokenizer("tokenizer()") check_in_list("check_in_list()") remove_in_list("remove_in_list()") get_current("get_current()") build_song("build_song()") end %% 连接 main --> load_list load_list --> get_text main --> write_list main --> is_all_digits main --> tokenizer main --> check_in_list main --> remove_in_list main --> get_current main --> build_song remove_in_list --> check_in_list
难点
-
正确加载每个参数很困难。例如,歌曲可以包含空格,歌手的名字也可以有空格或其他内容。但幸运的是,我们不需要太在意中间部分。我的意思是第一个参数总是命令,其余的部分是需要添加或删除的歌曲信息。我通过空格将参数/歌曲分割成各个部分:
<action> <song> <location>
并根据需要取每个部分。 -
当我在移动/添加歌曲时,可能该歌曲在播放列表文件中已经有一个
current
标记了(例如,在 playlist_tiny1.txt 文件中)。如果只检查歌曲名称的话,它将无法通过一些测试用例。例如,这是我对move
命令的处理方式。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
if (tokens[0] == "move") { if (is_all_digits(tokens.back())){ // 设置目标位置 int dest = std::stoi(tokens.back()); // 从 tokens 构建歌曲 std::string song; song = build_song(tokens, 1, tokens.size() - 1); + // 如果歌曲名称有 current 标记,则修复歌曲名称 + if (!check_in_list(song, playlist) && + !check_in_list(song + " current", playlist)) {continue;} + else if (check_in_list(song + " current", playlist)) { + song += " current"; + } remove_in_list(song, playlist); playlist.insert(playlist.begin() + dest - 1, song); } else { std::cout << "ERROR: Missing move destination" << std::endl; continue; } }
我在实际添加到播放列表之前,又检查了歌曲加上
current
是否存在于播放列表中。
解决方案
nyplaylists.cpp
|
|
相关内容
- CSCI 1200 - 作业 2 - 设计一个简单的 Uber
- CSCI 1100 - 作业 8 - 熊、浆果与游客重聚:类
- CSCI 1100 - 作业 7 - 字典
- CSCI 1100 - 作业 6 - 文件、集合和文档分析
- CSCI 1100 - 作业5 - 嵌套列表、网格和路径规划