Для начала вынесем факт того, что у нас 12 куплетов в константу, это нам пригодится для дальнейшего объявления массивов и итерации по ним.
const NUMBER_OF_VERSES: usize = 12;
Заведем массив числительных, которые меняются в первых строчках куплетов.
const NUMERALS: [&str; NUMBER_OF_VERSES] = [
"first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth",
"11th", "12th",
];
Так же заведем массив, для повторяющихся строчек куплетов.
const LINES: [&str; NUMBER_OF_VERSES] = [
"And a partridge in a pear tree",
"Two turtle-doves",
"Three French hens",
"Four calling birds",
"Five golden rings (five golden rings)",
"Six geese a-laying",
"Seven swans a-swimming",
"Eight maids a-milking",
"Nine ladies dancing",
"Ten lords a-leaping",
"Eleven pipers piping",
"12 drummers drumming",
];
Так же нам понадобится массив первых строчек куплетов, так как среди них есть отличающиеся. Я нашел такие в 1 и 11 куплете (так как массивы у нас индексируются с 0, то они будут под индексами 0 и 10 соответственно). Остальные же будут такими же как в массиве LINES, а значит можно по экономить размер бинаря и занимаемую память за счет того что в массивах у нас только ссылки на строки, которые можно копировать.
Тут конечно можно написать что-то вроде
const DIFFERING_LINES: [&str; NUMBER_OF_VERSES] = [
"A partridge in a pear tree",
LINES[1],
LINES[2],
// ...
];
но это дикая копипаста, которая плохо читается и подвержена ошибкам.
Благо в Rust есть константные функции, которые могут выполняться в compile-time и возвращают константы, а значит можно наш константный массив сгенерировать. Правда константные функции довольно сильно ограничены, в них можно пользоваться лишь ветвлениями, циклами, простейшей арифметикой (в том числе над указателями, а значит и получать доступ к элементам массива по индексу). Так же можно вызывать другие константные функции. Цикл for нам к сожалению тут не доступен, так как он работает поверх итераторов, а методы
IntoIter::into_iter
и
Iterator::next
, которые он вызывает, не являются константными. Но цикл со счетчиком можно сделать и через while. По итогу получим такую функцию:
const fn gen_differing_lines() -> [&'static str; NUMBER_OF_VERSES] {
let mut i = 0;
let mut lines = [""; NUMBER_OF_VERSES];
while i < NUMBER_OF_VERSES {
lines[i] = match i {
0 => "A partridge in a pear tree",
10 => "I sent 11 pipers piping",
i => LINES[i],
};
i += 1;
}
lines
}
И инициализируем ей наш массив:
const DIFFERING_LINES: [&str; NUMBER_OF_VERSES] = gen_differing_lines();
Теперь еще особенность, в самих куплетах, строчки из LINES в них идут в обратном порядке. То есть для 4 куплета (индекс 3) нам помимо строчки DIFFERING_LINES[3] нам нужно напечатать строки под индексами 2, 1 и 0 из LINES. Для удобства вынесем печать строк из LINES в отдельную функцию:
fn print_verse(mut i: usize) {
while i > 0 {
i -= 1;
println!("{}", LINES[i]);
}
}
Ну и осталось написать основной код для печати:
fn main() {
for i in 0..NUMBER_OF_VERSES {
println!("On the {} day of Christmas", NUMERALS[i]);
println!("My true love sent to me");
println!("{}", DIFFERING_LINES[i]);
print_verse(i);
}
println!("{}", LINES[0]);
}
А полное решение можно посмотреть и запустить тут:
https://play.rust-lang.org/?version=stable&mode=re...