Фруктовый салат на Python решается функцией из шести строк: каждое название фрукта режем пополам через len(fruit) // 2, все кусочки складываем в один список, сортируем через sorted() и склеиваем "".join(). Для ["apple", "pear", "grapes"] функция возвращает apargrapepesple.
Весь код запускался на Python 3.14.3, вывод программ приведён как есть.
- Условие задачи
- Решение на Python
- Как получаются кусочки: разбор по шагам
- Другие варианты решения
- Три ошибки, на которых решение не проходит тесты
- Деление через / вместо //
- round() вместо целочисленного деления
- sort() вместо sorted()
- Граничные случаи: пустой список, одна буква, регистр
- Как проверить решение перед отправкой
- Сколько работает решение
- Частые вопросы
- Можно ли решить фруктовый салат без sorted()?
- Почему мой ответ не совпадает с ожидаемым?
- Работает ли решение с русскими названиями фруктов?
- В какой версии Python работает этот код?
Условие задачи
Задача с англоязычного тренажёра, в оригинале Fruit Salad. Нужно написать функцию fruit_salad(), которая получает список названий фруктов, разрезает каждое слово пополам, сортирует получившиеся кусочки по алфавиту и возвращает их одной строкой.
Если в названии нечётное число букв, правая половина должна быть длиннее левой: apple режется на ap и ple. Все названия даны строчными латинскими буквами.
| Вызов | Ожидаемый результат |
|---|---|
fruit_salad(["apple", "pear", "grapes"]) | "apargrapepesple" |
fruit_salad(["raspberries", "mango"]) | "erriesmangoraspb" |
fruit_salad(["banana"]) | "anaban" |
Второй пример самый полезный: в raspberries 11 букв, и на нём ломается решение, где середину слова ищут через round().
Решение на Python
def fruit_salad(fruits):
chunks = []
for fruit in fruits:
middle = len(fruit) // 2 # для нечётной длины правая часть длиннее
chunks.append(fruit[:middle])
chunks.append(fruit[middle:])
return "".join(sorted(chunks))
print(fruit_salad(["apple", "pear", "grapes"]))
print(fruit_salad(["raspberries", "mango"]))
print(fruit_salad(["banana"]))
Вывод:
apargrapepesple
erriesmangoraspb
anaban
Оператор // делит нацело и отбрасывает дробную часть вниз. Для apple это 5 // 2 = 2, срез fruit[:2] даёт ap, а fruit[2:] забирает всё остальное, то есть ple. Условие про длинную правую половину выполняется само, без отдельной проверки на чётность.
sorted() не трогает исходный список и возвращает новый, уже упорядоченный. Метод "".join() склеивает его элементы через пустую строку.
Как получаются кусочки: разбор по шагам
Этот вариант печатает каждый кусочек фрукта и порядок, в котором он попадает в салат.
fruits = ["apple", "pear", "grapes"]
chunks = []
for fruit in fruits:
middle = len(fruit) // 2
print(f"{fruit:<7} длина {len(fruit)}, середина {middle}: {fruit[:middle]!r} + {fruit[middle:]!r}")
chunks += [fruit[:middle], fruit[middle:]]
print(chunks)
print(sorted(chunks))
print("".join(sorted(chunks)))
apple длина 5, середина 2: 'ap' + 'ple'
pear длина 4, середина 2: 'pe' + 'ar'
grapes длина 6, середина 3: 'gra' + 'pes'
['ap', 'ple', 'pe', 'ar', 'gra', 'pes']
['ap', 'ar', 'gra', 'pe', 'pes', 'ple']
apargrapepesple
Строки Python сравнивает посимвольно, по кодам Unicode, которые возвращает ord(). У ap и ar первая буква одинаковая, решает вторая: p раньше r. Кусочек pe стоит перед pes, потому что он короче, а всё, что в нём есть, совпадает с началом pes.

Другие варианты решения
Цикл выше удобнее читать новичку. Тот же салат собирается и короче, через генераторное выражение внутри sorted():
def fruit_salad(fruits):
return "".join(sorted(
part
for fruit in fruits
for part in (fruit[:len(fruit) // 2], fruit[len(fruit) // 2:])
))
print(fruit_salad(["apple", "pear", "grapes"]))
print(fruit_salad(["raspberries", "mango"]))
print(fruit_salad(["banana"]))
apargrapepesple
erriesmangoraspb
anaban
Результат тот же, но середина считается дважды, а два for в одной скобке новичок читает с трудом. На собеседовании или в учебном репозитории мы бы оставили цикл.
Третий вариант выносит нарезку в отдельный генератор. Его удобно проверять отдельно от сортировки:
def chunks(fruits):
for fruit in fruits:
middle = len(fruit) // 2
yield fruit[:middle]
yield fruit[middle:]
def fruit_salad(fruits):
return "".join(sorted(chunks(fruits)))
print(list(chunks(["kiwi", "lime"])))
print(fruit_salad(["kiwi", "lime"]))
['ki', 'wi', 'li', 'me']
kilimewi
sorted() принимает любой итерируемый объект, поэтому генератор можно передать ему напрямую, без промежуточного списка.
Три ошибки, на которых решение не проходит тесты

Деление через / вместо //
Деление / двух целых чисел в Python 3 возвращает float, даже если они делятся нацело. Срез с дробным индексом не работает:
fruit = "apple"
middle = len(fruit) / 2
print(middle)
print(fruit[:middle])
2.5
Traceback (most recent call last):
File "e1.py", line 4, in <module>
print(fruit[:middle])
~~~~~^^^^^^^^^
TypeError: slice indices must be integers or None or have an __index__ method
Исправление одно: len(fruit) // 2. Обёртка int(len(fruit) / 2) тоже даст 2, но это лишний шаг.
round() вместо целочисленного деления
Эта ошибка хитрее, потому что первый пример проходит. Функция round() округляет половинки к ближайшему чётному числу: round(2.5) даёт 2, а round(5.5) даёт 6.
def salad_round(fruits):
chunks = []
for fruit in fruits:
middle = round(len(fruit) / 2) # ошибка: банковское округление
chunks += [fruit[:middle], fruit[middle:]]
return "".join(sorted(chunks))
print(salad_round(["apple", "pear", "grapes"]))
print(salad_round(["raspberries", "mango"]))
print(round(2.5), round(3.5), round(5.5))
apargrapepesple
mangoraspberries
2 4 6
В raspberries 11 букв, середина 5,5 округлилась до 6, и левая половина raspbe вышла длиннее правой rries. Кусочки после сортировки случайно сложились обратно в целые слова, а ожидался erriesmangoraspb.
Правило такое: половинку round() округляет к ближайшему чётному числу. Если половина длины слова дробная, а целая часть у неё нечётная, округление идёт вверх, и левая часть получается длиннее. Это слова из 3, 7, 11, 15 букв, так что короткие названия тоже под ударом:
for fruit in ["fig", "avocado", "apple", "raspberries"]:
n = len(fruit)
print(f"{fruit:<11} {n:>2} round: {round(n / 2)} //: {n // 2}")
fig 3 round: 2 //: 1
avocado 7 round: 4 //: 3
apple 5 round: 2 //: 2
raspberries 11 round: 6 //: 5
У fig функция round() отрезает слева fi вместо f, у avocado получается avoc вместо avo. Совпадения на apple случайны, поэтому середину слова мы бы считали только через //.

sort() вместо sorted()
Метод list.sort() сортирует список на месте и возвращает None. Если результат сразу передать в join(), программа упадёт:
chunks = ["ple", "ap", "pe", "ar"]
result = chunks.sort()
print(result)
print("".join(result))
None
Traceback (most recent call last):
File "e2.py", line 4, in <module>
print("".join(result))
~~~~~~~^^^^^^^^
TypeError: can only join an iterable
Со sort() работать можно, но в две строки: сначала chunks.sort(), потом "".join(chunks). Когда отсортированный список нужен один раз и сразу уходит в склейку, удобнее sorted().
Граничные случаи: пустой список, одна буква, регистр
По условию всё приходит строчными латинскими буквами. Настоящие данные бывают другими. Вызовы ниже идут к функции fruit_salad() из раздела «Решение на Python».
print(repr(fruit_salad([])))
print(repr(fruit_salad(["fig", "a"])))
print(fruit_salad(["banana", "Cherry"]))
print(ord("C"), ord("b"))
print(fruit_salad(["яблоко", "ёжевика", "груша"]))
print(ord("а"), ord("я"), ord("ё"))
''
'afig'
Cheanabanrry
67 98
викагрокоушаяблёже
1072 1103 1105
Пустой список даёт пустую строку, без ошибки. У однобуквенного a левая половина пустая: len("a") // 2 даёт середину 0, срез [:0] возвращает '', и в салат попадает только a.

Заглавные латинские буквы идут раньше строчных: код C равен 67, а b равен 98. Поэтому Che встал первым, хотя по алфавиту ana и ban должны идти раньше него. С русскими названиями своя ловушка: у ё код 1105, у я 1103, и кусочек ёже уехал в самый конец.
Чинить это лучше ключом сортировки, а не переделкой строк: сами кусочки в ответе остаются как были.
def fruit_salad(fruits):
chunks = []
for fruit in fruits:
middle = len(fruit) // 2
chunks += [fruit[:middle], fruit[middle:]]
return "".join(sorted(chunks, key=str.casefold))
print(fruit_salad(["Apple", "banana", "Cherry"]))
print(sorted(["Ap", "ple", "ban", "ana", "Che", "rry"]))
anaApbanCheplerry
['Ap', 'Che', 'ana', 'ban', 'ple', 'rry']
Для буквы ё ключ другой, замена на е только при сравнении:
chunks = ["вика", "гр", "око", "уша", "ябл", "ёже"]
print(sorted(chunks))
print(sorted(chunks, key=lambda s: s.replace("ё", "е")))
['вика', 'гр', 'око', 'уша', 'ябл', 'ёже']
['вика', 'гр', 'ёже', 'око', 'уша', 'ябл']
Сортировка в Python устойчивая: кусочки с одинаковым ключом сохраняют исходный порядок. В задаче с тренажёра все названия строчные латинские, там ключ ничего не изменит.
Как проверить решение перед отправкой
Примеры из условия проще прогнать у себя через assert, чем отправлять код наугад и гадать по сообщению о непройденном тесте.
def fruit_salad(fruits):
chunks = []
for fruit in fruits:
middle = len(fruit) // 2
chunks.append(fruit[:middle])
chunks.append(fruit[middle:])
return "".join(sorted(chunks))
cases = [
(["apple", "pear", "grapes"], "apargrapepesple"),
(["raspberries", "mango"], "erriesmangoraspb"),
(["banana"], "anaban"),
([], ""),
]
for fruits, expected in cases:
result = fruit_salad(fruits)
assert result == expected, f"{fruits}: ждали {expected!r}, получили {result!r}"
print("Все", len(cases), "проверки прошли")
Все 4 проверки прошли
Если подставить версию с round(), второй assert остановит программу с сообщением AssertionError: ['raspberries', 'mango']: ждали 'erriesmangoraspb', получили 'mangoraspberries'.
Когда список фруктов нужно читать с клавиатуры, например в школьной системе проверки, названия вводятся одной строкой через пробел. Функция та же, меняется только вызов:
fruits = input().split()
print(fruit_salad(fruits))
На ввод apple pear grapes программа печатает:
apargrapepesple

Сколько работает решение
Из n фруктов получается 2n кусочков. Нарезка проходит по списку один раз. Дольше всего идёт сортировка: алгоритм на сравнениях в среднем не может обойтись меньше чем примерно n·log n сравнений, а каждое сравнение двух строк идёт по символам до первого различия. В примерах из условия не больше трёх фруктов, так что скорость здесь роли не играет.
Частые вопросы
Можно ли решить фруктовый салат без sorted()?
Можно отсортировать список методом chunks.sort(), а потом склеить. Свою сортировку пузырьком писать не нужно: пузырёк делает порядка n² сравнений, а встроенной сортировке хватает порядка n·log n.
Почему мой ответ не совпадает с ожидаемым?
Проверьте два места. Середина должна считаться через //: round() округляет половинки к чётному числу, и у слов из 3, 7, 11, 15 букв левая часть выходит длиннее правой. Сортировать нужно общий список всех половинок одним вызовом sorted(): если упорядочить кусочки каждого фрукта по отдельности, порядок в строке выйдет другим.
Работает ли решение с русскими названиями фруктов?
Работает: срезы и sorted() обрабатывают русские буквы так же, как латинские. Порядок при этом идёт по кодам символов, и буква ё окажется после я. Если нужен словарный порядок, передайте в sorted() ключ с заменой ё на е.
В какой версии Python работает этот код?
Весь код из статьи запускался на Python 3.14.3. Само решение обходится срезами, //, sorted() и join(), ничего из новых версий языка в нём нет. Строки вида f"..." в отладочных примерах появились в Python 3.6, на более старых версиях эти примеры не запустятся.








