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


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


Как в сишке не знаю, но в С++ rvo часть стандарта
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: return1, return2
От: Hоmunculus  
Дата: 21.01.24 11:35
Оценка: -3
Здравствуйте, rudzuk, Вы писали:

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

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


Абассаца... Дофига времени наэкономл на декларациях?
avalon/3.0.2
Re[3]: return1, return2
От: rudzuk  
Дата: 21.01.24 11:37
Оценка: +5
Здравствуйте, 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[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: 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[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[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: 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: 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[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 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.