Re[3]: return1, return2
От: rudzuk  
Дата: 21.01.24 11:37
Оценка: +5
Здравствуйте, Hоmunculus, Вы писали:

H> Да знаю я и про структуры и про классы. Получится лютый треш, если вокруг каждой из возможных комбинаций типов городить такое.


Не получится треша. Получится хорошо читаемый и легко поддерживаемый код.
avalon/3.0.2
Re[3]: return1, return2
От: T4r4sB Россия  
Дата: 21.01.24 10:59
Оценка: 2 (1) +3
Здравствуйте, Hоmunculus, Вы писали:

H>Здравствуйте, T4r4sB, Вы писали:


H>А, ну да. В Питоне вроде видел такое. Это питоновский кортеж?


Так-то это код на Русте.
На С++ это не сильно сложнее кстати
std::tuple<int,string,int*>   MyFunc()
{
   int res1 = 5;
   string res2 = “ssssss”;
   int* res3 = (int*)malloc…;

   return {res1,res2,res3};
}
...

auto tuple = MyFunc(); // так можно
auto [some_it, some_string, some_pointer] = MyFunc(); // так тоже можно начиная с ++17
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re: return1, return2
От: Буравчик Россия  
Дата: 21.01.24 10:43
Оценка: +4
Здравствуйте, Hоmunculus, Вы писали:

H>Почему не придумали типа такого?


Придумали.
1. Можно возвращать tuple из функции
2. Корутины, см. ключевое слово yield в разных языках программирования
Best regards, Буравчик
Отредактировано 21.01.2024 10:44 Буравчик . Предыдущая версия .
Re: return1, return2
От: T4r4sB Россия  
Дата: 21.01.24 10:46
Оценка: +4
Здравствуйте, Hоmunculus, Вы писали:


H>[ccode]


H>int/string/int* MyFunc()

H>{
H> int res1 = 5;
H> string res2 = “ssssss”;
H> int* res3 = (int*)malloc…;

H> return1 res1;

H> return2 res2;
H> return3 res3;
H>}


fn FyMunc() -> (i32, String, &'static i32) {
    let res1 = 5;
    let res2 = "ssssss";
    let res3 = &glob;
    return (res1, res2, res3);
}
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[5]: return1, return2
От: Буравчик Россия  
Дата: 21.01.24 10:50
Оценка: +3
Здравствуйте, Hоmunculus, Вы писали:

H>У нас есть тяжелая функция. Вызывать ее несколько раз — накладно. Она должна за один проход вычислить кучу всего и отдать сразу же при вызове.

H>Да, и про классы я знаю. Городить класс вокруг каждой тяжелой функции — тоже не то

Ну туплы (tuples) же. Давно придумано
Best regards, Буравчик
Re[2]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 11:35
Оценка: -3
Здравствуйте, rudzuk, Вы писали:

Да знаю я и про структуры и про классы. Получится лютый треш, если вокруг каждой из возможных комбинаций типов городить такое.
Re[3]: return1, return2
От: Буравчик Россия  
Дата: 21.01.24 11:00
Оценка: 2 (1) +1
Здравствуйте, Hоmunculus, Вы писали:

H>Здравствуйте, T4r4sB, Вы писали:


H>А, ну да. В Питоне вроде видел такое. Это питоновский кортеж?


Нет, это растовский кортеж. Но такое есть в куче современных языков
Best regards, Буравчик
Re: return1, return2
От: Evgeny.Panasyuk Россия  
Дата: 21.01.24 10:38
Оценка: +2
Здравствуйте, Hоmunculus, Вы писали:

H>Исторически сложилось, что функция возвращает одно значение. В return.


В некоторых языках она ещё и принимает только одно значение см карринг

H>Почему не придумали типа такого?


Наверное потому что синтаксически громоздко, и тривиально решается кортежами, а ещё лучше полноценными структурами с именованными полями.
Re[2]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 11:30
Оценка: :))
Здравствуйте, velkin, Вы писали:

Городить отдельную структуру для каждой комбинации выходных типов — слишком накладно.
Re[3]: return1, return2
От: rudzuk  
Дата: 21.01.24 11:36
Оценка: :))
Здравствуйте, Hоmunculus, Вы писали:

H> Городить отдельную структуру для каждой комбинации выходных типов — слишком накладно.


Абассаца... Дофига времени наэкономл на декларациях?
avalon/3.0.2
Re[3]: return1, return2
От: Evgeny.Panasyuk Россия  
Дата: 21.01.24 11:53
Оценка: +2
Здравствуйте, Hоmunculus, Вы писали:

EP>>Наверное потому что синтаксически громоздко, и тривиально решается кортежами, а ещё лучше полноценными структурами с именованными полями.

H>Нет. Я например сейчас разбираюсь в одной визуально программируемой штуке на нодах графа. Так вот там бывает, что у ноды три выхода, а у следующей ноды три входа. И они прям один к другому коннектятся
H>Хотя можно каждый выход и по отдельности использовать. То есть в коде это выглядело бы просто вызов одной функции как аргумент другой. Одна строчка. А вот с обычным подходом надо кучу кода городить

В Python достаточно добавить звёздочку:
>>> def f(): return 1,2,3
...
>>> def g(a,b,c): print(a,b,c)
...
>>> g(*f())
1 2 3

В других языках более громоздкие std::apply и подобное.

В принципе можно было бы завести специальный тип кортежа, который бы автоматически разбрасывался языком по аргументам. Но

H>И они прям один к другому коннектятся


В таких случаях намного лучше, читаемей и безопасней структуры, нежели безымянные кортежи (явные или внедрённые в язык для множественных значений возврата — не суть).
Ибо структура:
1. сама по себе является отдельным типом, что улучшает читаемость, type safety и type richness, что например позволяет делать перегрузку по разным типа структур.
2. поля структуры именованы, что улучшает читаемость и опять-таки safety. Вот например std::map::insert возвращает std::pair — это всего два значения, но всё же я искал ответ множество раз, ибо нет мнемоники за что зацепиться.
Re[3]: return1, return2
От: hi_octane Беларусь  
Дата: 21.01.24 15:21
Оценка: +2
H>То есть в коде это выглядело бы просто вызов одной функции как аргумент другой. Одна строчка. А вот с обычным подходом надо кучу кода городить
Ну эта возможность должна быть базовая для функциональных языков. Так что много где она есть.

Проверил C# — туплы поддерживает уже несколько лет, но именно фичи "разобрать тупл в параметры" почему-то не сделали. На практике, так чтобы прямо точно совпадали и типы тупла и порядок аргументов функции, и смысл, наверное достаточно редко случается (думаю в MS прямо по кодовой базе гитхаба проверили), и поэтому решили что им невыгодно это писать. Может ждут pull-request от комьюнити.

Если проблема частая, то самопальным SourceGenerator-ом можно в C# для функций генерировать обёртки, которые примут тупл, разложат, и вызовут. Т.е. технически уже сейчас даже в C# для себя сделать можно.

Проверил Nemerle — он эту фичу умел с самого начала.
using System;
public class Test
{
    static fn() : int*string*double
        { (1, "one", 1.0) }
        
    static getName(key : int, name : string, len : double) : string
        { name }
    
    static Main () : void
    {
        Console.WriteLine (getName(fn()));
    }
}
Re[3]: return1, return2
От: Буравчик Россия  
Дата: 21.01.24 10:45
Оценка: +1
Здравствуйте, Hоmunculus, Вы писали:

H>Я знаю про них. Нет. Не то. Прочитай мой пример выше.


Прочитал, не вижу разницы. Поясни
Best regards, Буравчик
Re[3]: return1, return2
От: velkin Удмуртия https://kisa.biz
Дата: 21.01.24 11:31
Оценка: +1
Здравствуйте, T4r4sB, Вы писали:

V>>Я сейчас не говорю про лишнее копирование, то есть возврат по значению, а не по ссылке или ещё что-то такое.

TB>А разве это в скомпилированном коде будет отличаться от возврата по ссылке?

Меня Страуструп в его книге учил, что нельзя полагаться на особое предопределённое поведение компиляторов. Какая это реализация компилятора, какой версии, какие флаги оптимизации и тому подобное.

Потому про компилятор и что у него "под капотом" ничего не скажу. Я веду речь только про то, что программист явно указывает в коде. А указать он может передача это по значению, по ссылке или по указателю.
Re[4]: return1, return2
От: T4r4sB Россия  
Дата: 21.01.24 11:35
Оценка: +1
Здравствуйте, velkin, Вы писали:


V>Меня Страуструп в его книге учил, что нельзя полагаться на особое предопределённое поведение компиляторов.


Как в сишке не знаю, но в С++ rvo часть стандарта
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re: return1, return2
От: scf  
Дата: 21.01.24 13:05
Оценка: +1
Здравствуйте, Hоmunculus, Вы писали:

H>Почему не придумали типа такого?


Топик не читал, но если вдруг еще не упомянули Scala:
def MyFunc(): (Int, String, Array[Int]) = 
  val res1 = 5
  val res2 = "ssssss"
  val res3 = Array[Int]()

  (res1, res2, res3)

printn(MyFunc()._2)
val b = MyFunc()._1

val (i, s, arr) = MyFunc()
Re: return1, return2
От: Pavel Dvorkin Россия  
Дата: 21.01.24 13:42
Оценка: +1
Здравствуйте, Hоmunculus, Вы писали:

H>Почему не придумали типа такого?


H>

H>int/string/int*   MyFunc()
H>{
H>   int res1 = 5;
H>   string res2 = “ssssss”;
H>   int* res3 = (int*)malloc…;

H>   return1 res1;
H>   return2 res2;
H>   return3 res3;
H>}

H>


Про turple тут уже сказали.

А именно так — а где все же выход из функции ? Ведь return тут вовсе не выход, а присваивание некоей скрытой переменной result1, result2...
А настоящий return будет лишь закрывающей фигурной скобкой. А если я хочу именно выйти из функции внутри какого-то if — else — for... ?

В интерпретаторе такое можно позволить. Пусть он отслеживает, каким result присваивались значения и при выходе проверяет, всем ли присвоено.

В компиляторе — нет. Совершенно ненужные накладные расходы.
With best regards
Pavel Dvorkin
Re[2]: return1, return2
От: mike_rs Россия  
Дата: 21.01.24 14:06
Оценка: +1
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>А настоящий return будет лишь закрывающей фигурной скобкой. А если я хочу именно выйти из функции внутри какого-то if — else — for... ?


ну если исходить из логики ТС то у него должно быть return res2/res2/res3; что автоматически нас приводит к кортежам, которые поддерживаются всеми языками, а где не поддерживаются — есть структуры.
Re: return1, return2
От: alpha21264 СССР  
Дата: 21.01.24 22:00
Оценка: :)
Здравствуйте, Hоmunculus, Вы писали:

H>Исторически сложилось, что функция возвращает одно значение. В return.

H>Да, разумеется я знаю по ссылочные аргументы, про in/out аргументы и прочее изменение входных параметров. Но! Return все равно один.
H>То есть если рассматривать функцию как черный ящик, то выход у этого ящика всегда один.

Ну массив чтоли верни. Или std::vector.

Течёт вода Кубань-реки куда велят большевики.
Re: return1, return2
От: Pzz Россия https://github.com/alexpevzner
Дата: 21.01.24 22:22
Оценка: +1
Здравствуйте, Hоmunculus, Вы писали:

H>Почему не придумали типа такого?


Придумали.
func foo() (int, int, int) {
    return 1, 2, 3
}

- - -

a, b, c = foo()


Это Go.

Из забавного, чтобы поменять местами значения a и b можно написать:
a, b = b, a
Re[3]: return1, return2
От: Pavel Dvorkin Россия  
Дата: 24.01.24 04:03
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Тут даже в не в расходах компилятора дело. Современный компилятор типа C# при возврате туплов проверяет definite assignment. Дело в расходе умственной энергии программиста.


Согласен.

В годы моей молодости в одной весьма в целом неплохой книге была дана рекомендация по написанию функций — один вход, один выход. То есть в терминах C-like языков — вход только через заголовок, выход только через последнюю }

Идея одного выхода оказалась не лучшей. Действительно, при, например, линейном поиске проще сделать return i внутри цикла, когда нашли, а конце return -1, так как не нашли.

А вот идея одного входа практически везде сейчас. В фортране можно было иметь несколько входов , чтобы не писать несколько функций с одним и тем же почти кодом. Но сейчас это решается выделением общего кода в отдельную приватную функцию и вызовом ее везде. С несколькими входами та же проблема — черт его знает, как мы сюда попали, через какой вход.
With best regards
Pavel Dvorkin
Отредактировано 24.01.2024 5:10 Pavel Dvorkin . Предыдущая версия .
Re[2]: return1, return2
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.01.24 04:53
Оценка: +1
Здравствуйте, diez_p, Вы писали:


_>в шарпах ref/out,

В шарпах, как и у всех — туплы. И destructuring assignment тоже есть.
(int, string, int*) MyFunc()
{
   return (5, "ssssss", malloc(...));
}

var (x, y, z) = MyFunc();
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: return1, return2
От: akasoft Россия  
Дата: 03.08.24 08:45
Оценка: :)
Здравствуйте, Hоmunculus, Вы писали:

H>Исторически сложилось, что функция возвращает одно значение. В return.

H>Да, разумеется я знаю по ссылочные аргументы, про in/out аргументы и прочее изменение входных параметров. Но! Return все равно один.
H>То есть если рассматривать функцию как черный ящик, то выход у этого ящика всегда один.

H>Почему не придумали типа такого?


Все ЯП так или иначе соответствуют возможностям ассемблера и инструкциям процессора.

У процессора есть возможность вызова кода (call с вариантами получения адреса точки вызова), при этом адрес следующей за вызовом инструкции call помещается в стек и появляется возможность вернуться после вызова обратно и продолжить. Для возврата есть своя инструкция (ret с вариантами), берущая из стека адрес, ранее сохранённый там call.

И вот эта инструкция ret — она одна, и return в функциях прямо ей соответствует.
Повторение ret после ret бессмысленно с точки зрения управления последовательностью выполнения инструкций.

А дальше навешивались правила, какие регистры и как можно использовать и в качестве чего, как ещё использовать стек и указатели на данные. Как передавать параметры и получать результат.

Другими словами, управление последовательностью выполнения инструкций процессора отдельно, а параметры отдельно. Но лукавые авторы ЯП посчитали, что они смогут их гармонично смешать. Круги на воде от результата их гармонии мы до сих пор наблюдаем.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>> SQL DE 2016
Re: return1, return2
От: Pzz Россия https://github.com/alexpevzner
Дата: 03.08.24 09:01
Оценка: +1
Здравствуйте, 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 в разных сочетаниях встречаются в разных путях программы?
return1, return2
От: Hоmunculus  
Дата: 21.01.24 10:31
Оценка:
Исторически сложилось, что функция возвращает одно значение. В return.
Да, разумеется я знаю по ссылочные аргументы, про in/out аргументы и прочее изменение входных параметров. Но! Return все равно один.
То есть если рассматривать функцию как черный ящик, то выход у этого ящика всегда один.

Почему не придумали типа такого?


int/string/int*   MyFunc()
{
   int res1 = 5;
   string res2 = “ssssss”;
   int* res3 = (int*)malloc…;

   return1 res1;
   return2 res2;
   return3 res3;
}

Print(MyFunc()2);

int b = MyFunc()1 + 6;
Re[2]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 10:42
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Наверное потому что синтаксически громоздко, и тривиально решается кортежами, а ещё лучше полноценными структурами с именованными полями.


Нет. Я например сейчас разбираюсь в одной визуально программируемой штуке на нодах графа. Так вот там бывает, что у ноды три выхода, а у следующей ноды три входа. И они прям один к другому коннектятся. Хотя можно каждый выход и по отдельности использовать. То есть в коде это выглядело бы просто вызов одной функции как аргумент другой. Одна строчка. А вот с обычным подходом надо кучу кода городить
Re[2]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 10:43
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Здравствуйте, Hоmunculus, Вы писали:


H>>Почему не придумали типа такого?


Б>Придумали. Называется корутины. См. ключевое слово yield в разных языках программирования


Я знаю про них. Нет. Не то. Прочитай мой пример выше.
Re[4]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 10:48
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>Здравствуйте, Hоmunculus, Вы писали:


H>>Я знаю про них. Нет. Не то. Прочитай мой пример выше.


Б>Прочитал, не вижу разницы. Поясни


У нас есть тяжелая функция. Вызывать ее несколько раз — накладно. Она должна за один проход вычислить кучу всего и отдать сразу же при вызове.

Да, и про классы я знаю. Городить класс вокруг каждой тяжелой функции — тоже не то
Re[2]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 10:53
Оценка:
Здравствуйте, T4r4sB, Вы писали:

А, ну да. В Питоне вроде видел такое. Это питоновский кортеж?
Re[4]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 11:00
Оценка:
Здравствуйте, T4r4sB, Вы писали:

Ну да. Тупанул. Действительно.
Re: return1, return2
От: velkin Удмуртия https://kisa.biz
Дата: 21.01.24 11:19
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Почему не придумали типа такого?

int/string/int*   MyFunc()
{
   int res1 = 5;
   string res2 = “ssssss”;
   int* res3 = (int*)malloc…;

   return1 res1;
   return2 res2;
   return3 res3;
}
Print(MyFunc()2);
int b = MyFunc()1 + 6;

Я тоже над этим думал, но вероятно нужно читать историю создания языка, причём языка Си откуда и пошёл C++. А в Си ещё так же нет функционального блока в отличие от Structured Text.

Ну вот смотри я сейчас наговнокодил.
struct.c
#include <stdio.h>

struct xxx
{
    int a;
    int b;
};

struct xxx func()
{
    struct xxx rrr;
    rrr.a = 33;
    rrr.b = 23;
    return rrr;
}

int main()
{
    struct xxx yyy = func();
    printf("%d, %d", yyy.a, yyy.b);
    return 0;
}

Результат
33, 23

Я сейчас не говорю про лишнее копирование, то есть возврат по значению, а не по ссылке или ещё что-то такое. Но даже пользователи Си могут имитировать возврат множества аргументов, функциональный блок и прочее, причём разными способами.

Мне думается, что твой пример мог бы быть таким.
int res1, string res2, int* res3 MyFunc()
{
   int res1 = 5;
   string res2 = “ssssss”;
   int* res3 = (int*)malloc…;

   return res1, res2, res3;
}

По крайне мере именованные возвращаемые значения выглядят более логично, к тому же происходит возврат всех аргументов в одном месте.

Но с другой стороны можно вернуть значения в параметрах функции, можно использовать структуру. И я не думаю, что пользователи Си над этим заморачиваются.

Если уж философствовать, то почему бы всегда не передавать аргументы в функцию в структуре. Тогда получится единый подход к передаче данных.

Re[2]: return1, return2
От: T4r4sB Россия  
Дата: 21.01.24 11:26
Оценка:
Здравствуйте, velkin, Вы писали:

V>Я сейчас не говорю про лишнее копирование, то есть возврат по значению, а не по ссылке или ещё что-то такое.


А разве это в скомпилированном коде будет отличаться от возврата по ссылке?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re: return1, return2
От: kov_serg Россия  
Дата: 21.01.24 11:30
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Почему не придумали типа такого?


Значит не нужно было.

H>То есть если рассматривать функцию как черный ящик, то выход у этого ящика всегда один.

Выходов может быть много. Вы не функции смотрите, а классы например там не только выходов, но и входов может быть сколько угодно.
Re: return1, return2
От: rudzuk  
Дата: 21.01.24 11:33
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H> Почему не придумали типа такого?


type
  TMyFuncResult = record
    v1 : Integer;
    v2 : string;
    v3 : Double;
  end;
  
function MyFunc : TMyFuncResult;
begin
  Result.v1 := 1;
  Result.v2 := '1';
  Result.v3 := 1.0;
end;

WriteLn(MyFunc.v2);
avalon/3.0.2
Re[2]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 11:34
Оценка:
Здравствуйте, kov_serg, Вы писали:

Городить класс вокруг любой комбинации типов — такое себе.
Re[5]: return1, return2
От: Evgeny.Panasyuk Россия  
Дата: 21.01.24 11:59
Оценка:
Здравствуйте, T4r4sB, Вы писали:

V>>Меня Страуструп в его книге учил, что нельзя полагаться на особое предопределённое поведение компиляторов.

TB>Как в сишке не знаю, но в С++ rvo часть стандарта

Начиная с С++17, оно ещё и гарантированно (что например позволяет возвращать значения не копируемых и не перемещаемых типов).
Re[2]: return1, return2
От: Evgeny.Panasyuk Россия  
Дата: 21.01.24 12:01
Оценка:
Здравствуйте, velkin, Вы писали:

V>Если уж философствовать, то почему бы всегда не передавать аргументы в функцию в структуре. Тогда получится единый подход к передаче данных.


Ну в некоторых языках у всех функций только один параметр
Re[3]: return1, return2
От: kov_serg Россия  
Дата: 21.01.24 12:04
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Городить класс вокруг любой комбинации типов — такое себе.

Это собственно говоря почему? У вас черный ящик и если вам нужна некая абстракция — класс самое то.
Если нужны комбинации есть шаблоны.
Re[3]: return1, return2
От: Pavel Dvorkin Россия  
Дата: 21.01.24 14:12
Оценка:
Здравствуйте, mike_rs, Вы писали:

_>ну если исходить из логики ТС то у него должно быть return res2/res2/res3; что автоматически нас приводит к кортежам, которые поддерживаются всеми языками, а где не поддерживаются — есть структуры.


Если да — это скрытый кортеж, верно. Превратить его в явный и дело с концом.

Но тогда просто return1.res1 превращается в tuple.field1 = res1, а return tuple где надо.
With best regards
Pavel Dvorkin
Re[3]: return1, return2
От: kov_serg Россия  
Дата: 21.01.24 14:24
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Городить класс вокруг любой комбинации типов — такое себе.

А смешивать нити исполнения в одну функцию это не так себе?
int/string/int*   MyFunc()
{
   int res1 = 5;
   string res2 = “ssssss”;
   int* res3 = (int*)malloc…;

   return1 res1;
   return2 res2;
   return3 res3;
}

Print(MyFunc()2);

int b = MyFunc()1 + 6;


Вот так можно на цпп написать то что вы хотите:
struct MyFunc {
    static int fn1() { return 5; }
    static string fn2() { return "ssssss"; }
    static int* fn3() { return (int*)malloc…; }
};

Print(MyFunc::fn2());

int b = MyFunc::fn1() + 6;
Re: return1, return2
От: diez_p  
Дата: 22.01.24 14:59
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Исторически сложилось, что функция возвращает одно значение. В return.

H>Да, разумеется я знаю по ссылочные аргументы, про in/out аргументы и прочее изменение входных параметров. Но! Return все равно один.
H>То есть если рассматривать функцию как черный ящик, то выход у этого ящика всегда один.

H>Почему не придумали типа такого?


H>

H>int/string/int*   MyFunc()
H>{
H>   int res1 = 5;
H>   string res2 = “ssssss”;
H>   int* res3 = (int*)malloc…;

H>   return1 res1;
H>   return2 res2;
H>   return3 res3;
H>}

H>Print(MyFunc()2);

H>int b = MyFunc()1 + 6;


H>


в go можно вернуть несколько значений https://gobyexample.com/multiple-return-values
и в javascript https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

в шарпах ref/out, а вот в java видимо придется делать обертку, но JIT должен это по идее заоптимизировать, т.к. непонятно как вернуть несколько параметров по значению, т.к. что ссылки, что примитивы возвращаются через копирование.
Отредактировано 22.01.2024 15:05 diez_p . Предыдущая версия .
Re: return1, return2
От: F3V  
Дата: 22.01.24 17:33
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Исторически сложилось, что функция возвращает одно значение. В return.

H>..
H>То есть если рассматривать функцию как черный ящик, то выход у этого ящика всегда один.

H>Почему не придумали типа такого?


Непонятно: тебе все три возврата одновременно нужны или каждый раз свой возврат при разных вызовах?

Если одновременно, то кортеж напрашивается (уже подсказали выше).

Если не одновременно, то нужна диспетчеризация:

  Javascript
(()=>{
let MyFunc = (i) => {
    if (typeof i === 'number') {
        let res1 = 5;
        return res1;
    } else if (typeof i === 'string') {
        let res2 = 'ssssss';
        return res2;
    } else if (Array.isArray(i)) {
        let res3 = [1, 2, 3, 4, 5];
        return res3;
    } else {
        throw new Error(`Неправильный тип параметра запроса: ${typeof i}`);
    }
}

console.log(MyFunc(''));
console.log(MyFunc(1) + 6);
console.log(MyFunc([]));
})();
  C++17
#include <iostream>

template<typename T> T MyFunc(T a = T{})
{
    if constexpr (std::is_same<T, int>::value)
    {
        int res1 = 5;
        return res1;
    }
    if constexpr (std::is_same<T, std::string>::value)
    {
        std::string res2 = "ssssss";
        return res2;
    }
    if constexpr (std::is_same<T, int*>::value)
    {
        int* res3 = (int*)malloc(10);
        return res3;
    }
    throw std::exception("Not supported.");
}

int main()
{
    std::cout << "Hello MyFunc!\n";

    std::cout << (MyFunc(std::string())) << std::endl;

    std::cout << MyFunc(1) + 6 << std::endl;

    std::cout << MyFunc<int*>() << std::endl;
}
Отредактировано 22.01.2024 17:37 F3V . Предыдущая версия .
Re[2]: return1, return2
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.01.24 03:29
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>А именно так — а где все же выход из функции ? Ведь return тут вовсе не выход, а присваивание некоей скрытой переменной result1, result2...

PD>А настоящий return будет лишь закрывающей фигурной скобкой. А если я хочу именно выйти из функции внутри какого-то if — else — for... ?
Совершенно согласен. Если пытаться напилить язык, который работает именно с запрошенным синтаксисом, то возможен взрыв мозга.


int/string/int*   MyFunc()
{
   int res1 = 5;
   int* res3 = (int*)malloc…;

   return1 res1;
   if (res1 == 0)
     return2 "foo"
   else
     return2 "bar";

   for (int i =0; i< 42; i++)
      return3 res3++;
}


PD>В интерпретаторе такое можно позволить. Пусть он отслеживает, каким result присваивались значения и при выходе проверяет, всем ли присвоено.

То-то и оно. Внезапно оказывается, что вот такой код делает совершенно не то, чего ожидается:
int/int find(int[] array, int length, int value)
{ 
   for(int i=0; i<length; i++)
     if (array[i] == value)
     {
       return1 i;
       return2 value;
     }
   return1 -1;
   return2 0;
}


PD>В компиляторе — нет. Совершенно ненужные накладные расходы.

Тут даже в не в расходах компилятора дело. Современный компилятор типа C# при возврате туплов проверяет definite assignment. Дело в расходе умственной энергии программиста.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 29.01.2024 4:39 Sinclair . Предыдущая версия .
Re: return1, return2
От: fk0 Россия https://fk0.name
Дата: 29.04.24 09:01
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Исторически сложилось, что функция возвращает одно значение. В return.


В одном значении может быть что-то, что хранит в себе юнион с типом, или коллекцию значений.
Даже в голом C можно вернуть структуру. И нет смысла избегать этого, более того: возврат структур
по-значению для компилятора легче в плане генерации оптимального кода, чем работа с указателями.
Просто на некоторых старинных рахитектурах возврат структуры по-значению требовал выкрутасов
с копированием на стеке и этого избегали. Сейчас везде как правило возврат структуры компилятором
преобразуется в ту же передачу по-ссылке (на структуру которая будет возвращена, и память под
которую аллоцирована на стеке вызывающей функции), но при этом отлично оптимизируется в регистры
при случае.

H>Почему не придумали типа такого?


H>
H>int/string/int*   MyFunc()
H>{
H>   int res1 = 5;
H>   string res2 = “ssssss”;
H>   int* res3 = (int*)malloc…;
H>   return1 res1;
H>   return2 res2;
H>   return3 res3;
H>}

H>Print(MyFunc()2);
H>int b = MyFunc()1 + 6;
H>



struct Eklmn { int x; char *y; float z; } my_func(...)
{
    ...
    if (...)
        return (struct Eklmn){.x = 100500};
    if (...)
        return (struct Eklmn){.y = "xaxaxa");
    ...
}

...
printf("%s", my_func(...).y);
int b = my_func(...).x + 6);
...



Если нужен std::variant, то можно сделать что-то вроде:


struct NedoVariant
{
    enum {
    VariantInt, VariantString, VariantFloat...
    } type;

    union {
        int x;
        const char *y;
        float z;
        ...
    };
};


Соответственно, устанавливаем/читаем поле type и другое соответствующее ему поле.
Re[3]: return1, return2
От: fk0 Россия https://fk0.name
Дата: 29.04.24 09:03
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Здравствуйте, velkin, Вы писали:


H>Городить отдельную структуру для каждой комбинации выходных типов — слишком накладно.


Проблема в том, что в убогих недоязыках отсутствует auto и шаблоны.
Re[5]: return1, return2
От: andrey.desman  
Дата: 08.05.24 15:32
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Да, и про классы я знаю. Городить класс вокруг каждой тяжелой функции — тоже не то


Зато именами полей они дают семантику, да и порядок знать не надо. С порядком вообще легко в лужу сесть, особенно если типы одинаковые.
В тех же плюсах в STL в новых классах перестали возвращать std::pair, и хорошо, что не начали возвращать туплы.
Re[3]: return1, return2
От: Pzz Россия https://github.com/alexpevzner
Дата: 03.08.24 09:07
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>А, ну да. В Питоне вроде видел такое. Это питоновский кортеж?


В питоне не бывает фигурных скобок. Там управление определяется тем, сколько пальцев помещается на неиспользованной левой стороне перфокарты.
Re[2]: return1, return2
От: Pzz Россия https://github.com/alexpevzner
Дата: 03.08.24 09:10
Оценка:
Здравствуйте, velkin, Вы писали:

V>Я тоже над этим думал, но вероятно нужно читать историю создания языка, причём языка Си откуда и пошёл C++. А в Си ещё так же нет функционального блока в отличие от Structured Text.


Авторы Си потом добавили множественные возвращаемые значения в свой следующий язык под названием Алеф. Потом это попало в Go, руками тех же идей. Очень подозреваю, что в Rust оттуда же попало.
Re[3]: return1, return2
От: Pzz Россия https://github.com/alexpevzner
Дата: 03.08.24 09:11
Оценка:
Здравствуйте, Hоmunculus, Вы писали:

H>Городить отдельную структуру для каждой комбинации выходных типов — слишком накладно.


Я б сказал, очень в глазах рябит, если этот тип в паре мест всего и нужен, а типов таких много. И затрудняет чтение кода.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.