Шаг 125.
Основы Kotlin.
Списки и множества. Задания для самостоятельного решения
На этом шаге мы приведем несколько практических заданий с возможными решениями.
Здесь мы предлагаем вам выполнить несколько заданий, направленных на закрепление изученного материала.
Задание 1: форматированный вывод меню таверны
Первое впечатление - самое важное, и одна из первых вещей, которые видит посетитель, - это меню. В этом задании сгенерируйте более аккуратную версию меню. Напечатайте пункты меню
с прописных букв и выровняйте по левому краю. Включите в меню цены, выровняйте их по десятичной точке. Отформатируйте все меню в аккуратный блок.
Вывод должен выглядеть так:
*** Welcome to Taernyl's Folly ***
Dragon's Breath...............5.91
Shirley temple................4.12
Goblet of la croix............1.22
Pickled camel hump............7.33
Iced boilermaker.............11.22
Раскрыть/скрыть решение и комментарии.
Алгоритм решения этой задачи может быть следующим:
для каждого пункта меню разобрать его на составляющие: тип, название и цена, тип отбросить, так как он нам не нужен; первую букву названия сделать строчной (большой); если
в цене после делятичной точки находится одна цифра, то добавить в конец 0, после чего все вывести, предварительно разместив необходимое количество точек между названием и ценой.
За максимальную длину строки меню возьмем его заголовок:
*** Welcome to Taernyl's Folly ***
Вид функции, реализующую указанную задачу, может быть таким:
fun printMenu()
{
// Вставляем пустую строку,
// чтобы отделить заголовок меню
println()
// Заголовок меню
val hello = "*** Welcome to Taernyl's Folly ***"
// Количество символов в заголовке меню
val cnt = hello.count()
// Выводим заголовок меню
println(hello)
println() // Пустая строка
// Цикл по количеству элементов меню
menuList.forEach {t ->
// "Разбирем" строку меню на элементы
val (_, name, price) = t.split(',')
// Делаем первую букву названия заглавной
val nameOut = name[0].toUpperCase() + name.substring(1, name.count())
// Определяем, сколько символов после десятичной точки
val pos = price.indexOf('.')
// Если он один, то добавляем 0
val priceOut = if (price.count() - pos == 2) {
price + '0'
} else {
price
}
// Дополняем название пункта меню нужным количество точек
val s = nameOut.forEach(cnt - priceOut.count(), '.')
// Печатаем пункт меню с точками, дополненный ценой
println(s + priceOut)
}
println() // Пустая строка для отделения меню
}

Рис.1. Текст функции, решающей указанную задачу
Прокомментируем приведенную функцию.
Можно было бы воспользоваться только изученными конструкциями, но нам показалось, что в этом случае текст будет более громоздким, поэтомумы решили воспользоваться
дополнительными конструкциями, предназначенными для работы со строками.
Прежде всего, мы определили переменную cnt, в которой находится длина заголовка меню. Далее организовали цикл по элементам списка menuList, в которм на каждой
итерации переменная t содержит очередную строку меню.
Эту строку мы разбили методом split() с разделителем "запятая" на три составляющие (тип, название и цена), первую из которых отбросили за ненадобностью. Далее сформировали
перменную nameOut из значения переменной name, преобразовав первую букву в заглавную и дописав все остальные без изменений.
Далее определили местоположение десятичной точки и подсчитали количество идущих после нее символов. Если оно оказалось равным 1 (не забывайте, что нумерация элементов строки начинается с 0!), то
добавляем в конец 0, в противном случае берем значение без изменений (переменная priceOut).
Подробнее остановимся на использовании метода padEnd(), который добавляет в конец строки символы, указанные в качестве второго параметра. Первый параметр определяет
общую длину строки, то есть метод добавляет столько символов, чтобы длина строки стала равной значению первого параметра (не забывайте, что нам еще нужно к этой строке
добавить цену, длина которой определяется значением priceOut.count()).
В заключение выводим сумму сформированных строк на экран. Результат работы функции приведен на рисунке 2.

Рис.2. Вывод форматированного меню
Задание 2: улучшенное форматирование меню таверны
На основе предыдущего кода для форматирования сгенерируйте меню, которое также группирует элементы в списке по виду. Вы должны получить следующий вывод:
*** Welcome to Taernyl's Folly ***
~[shandy]~
Dragon's Breath...............5.91
~[elixir]~
Shirley temple................4.12
Iced boilermaker.............11.22
~[meal]~
Goblet of la croix............1.22
~[desert dessert]~
Pickled camel hump............7.33
Раскрыть/скрыть решение и комментарии.
Идея, положенная в основу реализации этой функции, достаточно проста: заведем еще один, изначально пустой список, в который будет хранить виды пунктов меню. Если очередного
вида в этом списке нет, мы его туда помещаем и просматриваем список пунктов меню, выводя те пункты, которые совпадают с текущим видом.
Приведем текст этой функции.
fun printMenu2()
{
// Вставляем пустую строку,
// чтобы отделить заголовок меню
println()
// Заголовок меню
val hello = "*** Welcome to Taernyl's Folly ***"
// Количество символов в заголовке меню
val cnt = hello.count()
// Выводим заголовок меню
println(hello)
// Создаем изменяемый список с видами элементов меню
val typeList = mutableListOf<String>()
// Цикл по количеству элементов меню
menuList.forEach {t ->
// "Разбирем" строку меню на элементы
val (type, _, _) = t.split(',')
// Если такого элемента нет...
if (typeList.contains(type) == false)
{
// ... добавляем его в список
typeList.add(type)
//Формируем и выводим заголовок вида
val titleType = " ~[" + type + "]~"
println(titleType)
// Ищем все записи в таким видом
menuList.forEach {tt ->
val (type2, name, price) = tt.split(',')
// Если вид текущей записи совпадает с выбранным...
if (type == type2)
{
// ... выводим его в меню
// Делаем первую букву названия заглавной
val nameOut = name[0].toUpperCase() + name.substring(1,
name.count())
// Определяем, сколько символов после десятичной точки
val pos = price.indexOf('.')
// Если он один, то добавляем 0
val priceOut = if (price.count() - pos == 2) {
price + '0'
} else {
price
}
// Дополняем название пункта меню нужным количество точек
val s = nameOut.forEach(cnt - priceOut.count(), '.')
// Печатаем пункт меню с точками, дополненный ценой
println(s + priceOut)
}
}
}
}
println() // Пустая строка для отделения меню
}

Рис.3. Текст функции, решающей указанную задачу
Результат ее работы приведен на рисунке 4.

Рис.4. Вывод форматированного меню, сгруппированного по видам
Файл с проектом, дополненным разобранными на этом шаге функциями, можно взять здесь.
Со следующего шага мы начнем рассматривать ассоциативные массивы.
Предыдущий шаг
Содержание
Следующий шаг