снова про языковые конструкции
В общем, пришел в голову такой изврат:
Возвращаемое значение функции/метода в современных императивных языках программирования всегда одно. Мне же часто в коде требуется оформить определенным образом результат работы функции, а также некую дополнительную информацию, например код ошибки.
Конечно, существует множество "выходов из положения", но на мой взгляд лучший вариант - позволить функциям возвращать несколько значений.
Например:
if (error==DivisionErrors::NoError) {
printf("result is %d", i);
}else {
printf("error happened: %d", error);
}
В частности, если нужна только зеленая составляющая цвета:
Конечно, можно создать структуру (или класс) Color, но что если эта структура больше никогда не понадобится, кроме использования в качестве результата этой функции?
В частности, если нужна только зеленая составляющая цвета:
Языки с таким синтаксисом есть.
return 1, 2, 3
a, b, c = get_params()
print a, b, c
d = get_params()[2]
print d
_, f, _ = get_params()
print f
позволь, мы причем?
по приведенному примеру - используй механизм исключений
ничего страшного
а если только красная - то:
?
не?
С питоном тоже, но поковыряю его как нибудь
С питоном тоже, но поковыряю его как нибудь
На C++ можно воспользоваться ссылками или указателями. Например:
[CODE=с++]void get_color(int *red = 0, int *green = 0, int * blue = 0)
{
if (red) *red = 1;
if (green) *green = 2;
if (blue) *blue = 3;
}
int main () {
int red = 0;
get_color(&red);
cout << red << endl;
int green = 0;
get_color(0, &green);
cout << green << endl;
int blue = 0;
get_color(0, 0, &blue);
cout << blue << endl;
return 0;
}[/CODE]
Но тут есть свои недостатки. Код опаснее и не такой компактный.
Кроме того, можно попробовать использовать перегрузку функции. Но это может запутать пользователя.
return array($r, $g, $b);
}
list(,$green,) = getColor();
[CODE=c++]enum {RED, GREEN, BLUE};
vector<int> get_color()
{
vector<int> v(3);
v[RED] = 1;
v[GREEN] = 2;
v[BLUE] = 3;
return v;
}
int main () {
cout << get_color()[RED] << endl;
cout << get_color()[GREEN] << endl;
cout << get_color()[BLUE] << endl;
return 0;
}
[/CODE]
let (r,g,b) = getColor;;
let (_,g,_) = getColor;;
Остаётся лишь пожелать топикстартеру ознакомиться с другими языками и парадигмами.
2Kogrom: да уж, C++ как всегда рулит, и как всегда не туда...
[CODE=c++]enum {RED, GREEN, BLUE};
vector<int> get_color()
{
vector<int> v(3);
v[RED] = 1;
v[GREEN] = 2;
v[BLUE] = 3;
return v;
}
int main () {
cout << get_color()[RED] << endl;
cout << get_color()[GREEN] << endl;
cout << get_color()[BLUE] << endl;
return 0;
}
[/CODE]
Тогда уже лучше через map делать, мне сдается. =)
[CODE=C++]
map<string, int> get_color() {
map<string, int> ret;
ret["red"] = 1;
ret["green"] = 2;
ret["blue"] = 3;
return ret;
}
int main() {
cout << get_color()["green"] << endl;
return 0;
}
[/CODE]
Да, map возвращать по значению - ещё круче :)
Но я действительно зарулил не туда, так как это почти то, о чём автор сказал сразу, то есть возврат чего-то типа структуры. Что, кстати, тоже не является хорошим тоном.
Это же примитивно и не инновационно, да и сопли поразвешивать, рассуждая о "великом", не дает.
Я говорю про передачу по значению. Тут у нас будет создание дополнительных объектов и копирование (которое может быть вообще запрещено для некоторых классов).
Передача по значению - это нормально при работе с примитивными данными, но структуры или контейнеры обычно передают по ссылке, указателю (или используют интеллектуальные указатели).
Возвращение обычного указателя (который указывает на объект, созданный в куче) для C++ тоже не самая лучшая идея. Тут придется не забыть удалить объект из кучи.
ну или
В первом случае возвращаем список строк, во втором - чисел
Попробуйте Forth.
В примерах на Python и F# также возвращается одно значение - кортеж.
На мой взгляд, гораздо лучше возвращать подписанный XML-документ (вместе со схемой и сертификатом, чтоб наверняка).
Плохо то, что эту структуру предварительно приходится объявить. Даже если она нужна всего лишь для одного-единственного раза.
Хотя, возможно при возврате значения не всегда будет копирование и создание нового объекта. Вот этот код меня удивил (наверное сказалось то, что пол года на C++ не программировал):
[CODE=C++]struct MyClass
{
MyClass(){cout << "MyClass" << endl;}
~MyClass(){cout << "~MyClass" << endl;}
MyClass(const MyClass&)
{
cout << "copy constructor" << endl;
}
MyClass& operator=(const MyClass&)
{
cout << "operator =" << endl;
return *this;
}
};
MyClass func()
{
MyClass t;
return t;
}
int main () {
MyClass t = func();
return 0;
}
[/CODE]
выводится только один конструктор и деструктор...
Какие в этом есть недостатки?
Думаю, по хорошему, без базы данных не обойтись.
это g++ (4.4.1) так выдает. Кодепадовский же компилятор выдаёт другое:
http://codepad.org/IDQMT906
Чудеса.
Добавлено позже. У них тоже g++, но флаги другие. Видать, у них без оптимизации...
Это оптимизация возврата по значению. Она не входит в стандарт и может произойти, а может и нет - на усмотрение производителя компилятора. В оригинале она происходит при возврате анонимных объектов, а суть её более-менее раскрыта у Мейерса. Полагаться на неё всё равно нельзя в том смысле, что про конструктор копирования надо помнить вне зависимости от компилятора.
Никаких, но ведь автору нужно именно несколько? А в форте в натуре можно. Только там функций нет - в этом и состоит подвох. Ну вообще да, бугагашенька, нарна, не очевидна... просто даже в математике у функции одно значение. :)
А давайте перейдем в комплексную плоскость, там хоть 35 значений у функции не проблема. :)
функция - окружность, например.
если прочитать все - то несколько полей структуры его устраивает, но структуру он описывать не хочет лишний раз.
Наверное, это всё-таки будет одно комплексное значение?
Ln(z) = ln(|z|) + i*arg(z) + 2*Pi*k*i
k - произвольное целое число.
я не предлагал переходить на комплексную плоскость :)
x^2 + y^2 = 4
x = +(-) * (4-y^2)^(1/2)
математика пятый класс. При y = 0, имеется 2 решения уравнения +2 и -2
Ну и если мы опишем соответствующую функцию, принимающую один аргумент - то должны будем получить в результате массив из результатов, не?
В стандарт как раз входит (12.8/15), иначе компиляторы бы не имели права реализовывать такое. Только входит не в качестве обязательного требования, а в качестве рекомендации.
В оригинале она происходит при возврате анонимных объектов
Не ананимных тоже можно. Это уже Named RVO. И то и другое умеют почти все современные компиляторы, но фича и правда не обязательная к исполнению. )
В .Net 4.0 для этого есть Tuple<> (аля кортеж). Хотя избыточностью объявлений страдают все статически типизированные ЯП, в динамических попроще это.
п.с. имхо, для F# более корректным будет вариант через размеченное объединение:
type MyResult<'r, 'e> =
| MSome of 'r
| MErr of 'e
let my_fun x =
if x = 0 then
MSome x
else
MErr "err"
let my_call x =
match my_fun x with
| MSome z -> printfn "%A" z
| MErr z -> printfn "Error: %A" z
[<EntryPoint>]
let main _ =
my_call 1
my_call 0
Console.ReadLine()
0
Опишите, посмотрим.
И? Здесь мы просто получили значение из другого множества - значение на множестве, хрен с ним, римановых, плоскостей - не?
http://ru.wikipedia.org/wiki/Функция_(математика)
Да, но в таких языках, как C# и VB.NET нет паттерн-матчинга, отчего применение кортежей становится неудобно. А частичное применение - ваще вещь!
В правильных языках (да-да, я о нём ;)) есть мощный вывод типов. Благодаря ему типы в большинстве случаев можно не указывать, и код становится практически таким же коротким, как в динамических языках.
Ну, это ведь приходится заранее объявлять это объединение, что равноценно объявлению структуры. А именно этого хотел избежать топикстартер.
Хочу отметить, что тема навела меня на полезные размышления. Посему предлагаю наградить чем-нибудь ОПа, и забанить - во избежание раздувания дальнейшего холивара :).
Заодно простить Оксотника (никто не помнит Дафифу, не? :)).
Я всё больше склоняюсь к мнению, что вывод типов это не более чем игрушки, кроме случаев типа LINQ, где юзаются автоматически сгенерированные анонимные типы.
Реальность показывает, что паттерн-матчинг в реальности нужен редко(а вернее он удобен для небольшого класса задач). Т.к. расширять код основанный на операторе match-with тот ещё геморрой (поменял код объединения - скажи привет ручному переписыванию всех блоков match-with).
Есть http://msdn.microsoft.com/ru-ru/library/ee353439.aspx и пара вариантов(аля Tuple в C#). Но я не люблю их юзать просто (предпочитаю потратить 2 мин на ручное описание структуры объединения, мне так наглядней).
http://ru.wikipedia.org/wiki/Функция_(математика)
Что вы имеете в виду? Функция сама по себе по определению становится многозначной, я об этом.
LINQ - это кусочек функциональщины в императивном мире привычного дотнета. В полностью функциональном окружении вывод типов логичен и более чем удобен.
Ну, не знаю. Просто ты не умеешь его готовить. if'ы и switch'и точно так же придётся переписывать.
let (r,g,b) = getColor;;
Что тут неудобного? А ведь это именно паттерн-матчинг, когда возвращаемый кортеж разбирается и присваивается трём именам.
Да я не спорю, сам за наглядность.
Я говорю про большие куски кода, когда удобнее применить ООП, чем извращаться с паттерн матчингом. Локальный паттерн матчинг удобен да, но когда протягиваешь сложное объединение(5+ вариантов или даже несколько таких объединений) через несколько функций эта проблема вылезает.
Просто представь что будет если у тебя 3-4 объединения по 5+ вариантов и больше чем 2-3 match-with'а, которые эти объединения разбивают. Естественно, как вариант, можно собрать разборку в 1 функцию и сделать там частичное применение... но решение особо проще не станет(если вообще упростится). Такое уже надо расписывать через ООП. А небольшие match-with легко пишутся и через if-else\switch-case\if-ifelse-else.
Про локальный паттерн матчинг я ничего не говорю - вещь отличная. Но это лишь небольшой сахар, который существенного влияния на код не имеет (сделать то же через структуру элементарно).
Я думаю, что в чисто функциональном окружении он (вывод типов) банально необходим. Иначе мы бы сошли с ума прописывая типы (причём львиную долю времени этим и занимались бы, т.к. даже сейчас, когда есть вывод типов, иногда приходится в ручную прописывать типы).
Опять же по LINQ (не о сахарном синтаксисе from-in) - вывод типов там банально необходим, т.к. без него мы бы просто захлебнулись в объявлениях типов и весь профит от простоты был бы затёрт объявлениями типов.
Тем не менее, "баден-баден" тоже излишен: тип выражения всё равно выводится компиляторами большинства статически типизируемых языков хотя бы для проверки типов, а читаемость не страдает (ну разве что иногда полезно иметь базовый тип, но не для читаемости даже, а для мелкого рефакторинга), а с какой-нибудь STL без вывода типов вообще никуда (щас в одиннадцатом C++ можно заодно выводить не менее шаблонные типы возвращаемых значений). Иное дело, что достаточно вывода при инициализации и ни к чему выводить тип из использования, если бы не квазицитирование в "тех самых".
Блин, ну это одно значение. Просто "тип" у него такой :)
Объяснюсь: изначально я озвучил понятие кортежа в ироничной форме - оно не упоминалось явно, а просто "несколько значений" вон в Форте.
Приятно, что всплыла порция зачётного матана :)