Здравствуйте, Hоmunculus, Вы писали:
H>Исторически сложилось, что функция возвращает одно значение. В return.
В одном значении может быть что-то, что хранит в себе юнион с типом, или коллекцию значений.
Даже в голом C можно вернуть структуру. И нет смысла избегать этого, более того: возврат структур
по-значению для компилятора легче в плане генерации оптимального кода, чем работа с указателями.
Просто на некоторых старинных рахитектурах возврат структуры по-значению требовал выкрутасов
с копированием на стеке и этого избегали. Сейчас везде как правило возврат структуры компилятором
преобразуется в ту же передачу по-ссылке (на структуру которая будет возвращена, и память под
которую аллоцирована на стеке вызывающей функции), но при этом отлично оптимизируется в регистры
при случае.
H>Почему не придумали типа такого?
H>
Здравствуйте, Hоmunculus, Вы писали:
H>Здравствуйте, velkin, Вы писали:
H>Городить отдельную структуру для каждой комбинации выходных типов — слишком накладно.
Проблема в том, что в убогих недоязыках отсутствует auto и шаблоны.
Здравствуйте, Hоmunculus, Вы писали:
H>Да, и про классы я знаю. Городить класс вокруг каждой тяжелой функции — тоже не то
Зато именами полей они дают семантику, да и порядок знать не надо. С порядком вообще легко в лужу сесть, особенно если типы одинаковые.
В тех же плюсах в STL в новых классах перестали возвращать std::pair, и хорошо, что не начали возвращать туплы.
Здравствуйте, Hоmunculus, Вы писали:
H>Исторически сложилось, что функция возвращает одно значение. В return. H>Да, разумеется я знаю по ссылочные аргументы, про in/out аргументы и прочее изменение входных параметров. Но! Return все равно один. H>То есть если рассматривать функцию как черный ящик, то выход у этого ящика всегда один.
H>Почему не придумали типа такого?
Все ЯП так или иначе соответствуют возможностям ассемблера и инструкциям процессора.
У процессора есть возможность вызова кода (call с вариантами получения адреса точки вызова), при этом адрес следующей за вызовом инструкции call помещается в стек и появляется возможность вернуться после вызова обратно и продолжить. Для возврата есть своя инструкция (ret с вариантами), берущая из стека адрес, ранее сохранённый там call.
И вот эта инструкция ret — она одна, и return в функциях прямо ей соответствует.
Повторение ret после ret бессмысленно с точки зрения управления последовательностью выполнения инструкций.
А дальше навешивались правила, какие регистры и как можно использовать и в качестве чего, как ещё использовать стек и указатели на данные. Как передавать параметры и получать результат.
Другими словами, управление последовательностью выполнения инструкций процессора отдельно, а параметры отдельно. Но лукавые авторы ЯП посчитали, что они смогут их гармонично смешать. Круги на воде от результата их гармонии мы до сих пор наблюдаем.
Здравствуйте, Hоmunculus, Вы писали:
H>Почему не придумали типа такого?
Придумали.
func minmax(nums []int) (min, max int) {
if len(nums) > 0 {
min = nums[0]
max = nums[0]
for i := 1; i < len(nums); i++ {
n := nums[i]
if n < min {
min = n
}
if n > max {
max = n
}
}
}
return min, max
}
P.S. А что твой вариант должен делать, если return1, 2, 3 в разных сочетаниях встречаются в разных путях программы?
Здравствуйте, velkin, Вы писали:
V>Я тоже над этим думал, но вероятно нужно читать историю создания языка, причём языка Си откуда и пошёл C++. А в Си ещё так же нет функционального блока в отличие от Structured Text.
Авторы Си потом добавили множественные возвращаемые значения в свой следующий язык под названием Алеф. Потом это попало в Go, руками тех же идей. Очень подозреваю, что в Rust оттуда же попало.