LINUX.ORG.RU

В каких ЯПах существуют функции, способные возвращать несколько значений?

 , , ,


0

0

Вроде такого:

int, double, double add (int a) {
    return (a+1), (a+2.2), (a+3.3);
}

int a = 8;
int b;
double c, d;

b, c, d = add(a);

А вообще, в чем проявлялась бы неэффективность подобной возможности? Могу представить разве то, что она не так хорошо приспособлена к трансляции в ассемблерный код как функция с единичным возвращаемым значением. Так по конвенции все кладут результат в RAX да и все, а с множественными значениями придется еще что-то изобретать и ломать ABI.

Ответ:

Список япов:

  1. go
  2. lua
  3. haskell
  4. rust и проч.

Реализованы - через тульпы/структуры, поэтому в abi ничего не меняется. Но есть и более интересные вариации: так, например, в SBCL первые три возвращаемых значения передаются через регистры (RDX, RDI, RSI), а остальные через стек



Последнее исправление: x86- (всего исправлений: 6)
Ответ на: комментарий от x86-

да можно и через функцию, лишь-бы был смысл
а что вообще за побочки?

superuser ★★★★☆
()
Последнее исправление: superuser (всего исправлений: 1)
Ответ на: комментарий от fernandos

перепрыгуют в переменные?

получается переменные занимают память и время в некоторых языках тогда как можно считывать данные сразу из массива лишнего не создавая
тс может подобное хочет
нояниченезнаю, может сразу в массив без возврата тогда

nanosecond
()
Последнее исправление: nanosecond (всего исправлений: 1)
Ответ на: комментарий от nanosecond

процессор вообще оперирует только регистрами, размерность и число которых сильно ограничена, и ячейками памяти, и никакой там магии/массивов и т.д. не существует, так-что fernandos прав

superuser ★★★★☆
()
Последнее исправление: superuser (всего исправлений: 2)
Ответ на: комментарий от x86-

Насчёт плюсов, кстати, хз, тут нужно разбираться как именно structured binding будет оптимизирован компилятором, и будет ли вообще. То есть, в рантайме может и не быть никакой структуры.

WitcherGeralt ★★
()
Ответ на: комментарий от x86-

Но судя по тому, что это реализовано в каждом втором языке, фича чем-то полезна, хотя и является просто синтаксическим сахаром

это просто костыль для языков, где нет нормальной деструктуризации

anonymous
()
Ответ на: комментарий от x86-

Передать указатели на них в функцию =) Не убегай от сишки она тебя всё равно догонит, кусь ^.^

LINUX-ORG-RU ★★★★★
()

О

По соглашению в
SYSTEM V
APPLICATION
BINARY INTERFACE
Intel386™ Architecture
Processor Supplement


Function Calling Sequence


%eax Return value
x86-
() автор топика
Ответ на: комментарий от x86-

Такое вроде и в шарпах

Есть такое в решётке. Если параметр помечен out, его даже нельзя оставить неинициализированным - ошибка компилятора - технически похоже на ошибку not all control paths return value.

PhysShell ★★
()

Да много где. Возвращаешь коллекцию, делаешь деструктуризацию при присваивании.

Nervous ★★★★★
()

это детский сад. Какая разница с массивом? Если бы возвращала несколько значений нескольким получателям это было бы интересно.

no2700
()
Ответ на: комментарий от WitcherGeralt

Возвращается одно значение, а дальше ты делаешь деструктуринг

Я про сахар, вы про возможности, ок.

Это два разных явления, которые можно смешать только по недопониманию.

Это вы поняли по двум декларациям возвращаемого типа?

fernandos ★★★
()
Ответ на: комментарий от x86-

Integer return values up to 64 bits in size are stored in RAX while values up to 128 bit are stored in RAX and RDX. Floating-point return values are similarly stored in XMM0 and XMM1. The wider YMM and ZMM registers are used for passing and returning wider values in place of XMM when they exist.

Вот тебе пример, как LLVM оптимизирует:

test:
lea     eax, [rdi + 1]
lea     ecx, [rdi + 2]
lea     edx, [rdi + 3]
shl     rcx, 32
or      rax, rcx
ret
sudopacman ★★★★★
()

даже в баше можно передать в функцию массив и сделать с ним что-то

IvanR ★★★
()
Ответ на: очевидный общелисп от anonymous

вот тут ущербные кстати собрались, сравнивают сахар для возвращения массива с реальным возвращением нескольких значений

anonymous
()

В C++ Так можно. Начиная с C++17 таплы больше не делают больно:

auto foo(){
    return std::tuple{ 1, 3.14, "asd" };
}


auto [a, b, c] = foo();
SR_team ★★★★★
()
Ответ на: комментарий от x86-

Структура - это такой себе объект, можно считать. И в RAX все так же кладут ссылку на этот единый объект

  1. RAX + RDX - там 2 регистра для результата

  2. Если возвращать по значению, то структура будет на стеке

SR_team ★★★★★
()
Последнее исправление: SR_team (всего исправлений: 1)

В Common Lisp:

;; фунция возвращает два значения (можно больше)
(defun polar (x y) 
  (values (sqrt (+ (* x x) (* y y))) (atan y x))) 

Ну и, соответственно, есть ряд функций, которые принимают эти множественные значения. Например, multiple-value-bind.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 2)
Ответ на: комментарий от SR_team

auto [a, b, c] = foo();

реал скобкобляди это все-же плюсовики

anonymous
()

Лисп же. Остальные так, имитируют на полшишечки кортежами/массивами/списками.

no-such-file ★★★★★
()
Ответ на: комментарий от fernandos

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

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

no-such-file ★★★★★
()

А вообще, в чем проявлялась бы неэффективность подобной возможности?

А ты подумай как эту функцию реализовать и получишь ответ.

Ты не можешь заранее знать данные каких типов, каких размеров и в каком количестве ты будешь передавать таким способом.
Если же попытаться это сделать то тебе несомненно потребуется выделять участок памяти куда все эти данные складывать определённым образом...

Стоп, а это тебе случаем ничего не напоминает?
Что-то такое распространённое и являющиеся повсеместной практикой?

torvn77 ★★★★★
()

C++. Да и вообще любой где есть кортежи или где их можно запилить.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от SR_team

C++17

плюсы, как всегда, очень быстры в перенимании фич (нет)

anonymous
()
Ответ на: комментарий от Zubok

есть ряд функций, которые принимают эти множественные значения. Например, multiple-value-bind.

тю, в кложуре деструктуризация работает везде и прозрачно в любых binding-формах, в т.ч. и для ассоциативных коллекций

anonymous
()
Ответ на: комментарий от x86-

В шарпе поддержка кортежей на уровне языка:

using System;
public class Class {
    public (int x, int y) GetValues() {
        return (1, 2);
    }
}
amm
()
Ответ на: комментарий от grem

А в 11 и 14 было больно?

// C++11
std::tuple<int, double, const char*> foo() { return {1, 3.14, "asd"}; }

int main() {
    int a;
    double b;
    const char* c;
    std::tie(a,b,c) = foo();
}

https://gcc.godbolt.org/z/4b8Wbn4PK

В С++17 лучше на мой взгляд…

fsb4000 ★★★★★
()
Ответ на: комментарий от anonymous

C++42

это да, с++ ещё нас с тобой переживёт

anonymous
()
Ответ на: комментарий от anonymous

тю, в кложуре деструктуризация работает везде и прозрачно в любых binding-формах, в т.ч. и для ассоциативных коллекций

тю, а о чем ты говоришь? Это и в CL есть: DESTRUCTURING-BIND. http://clhs.lisp.se/Body/m_destru.htm

Здесь же другое - именно множественный результат. Если ты не будешь делать multiple-value-bind, то функция первое значение возвратит.

[1]> (defun polar (x y) 
  (values (sqrt (+ (* x x) (* y y))) (atan y x))) 
POLAR
[2]> (polar 1 2)
2.236068 ;
1.1071488
[3]> (setq *a* (polar 1 2))
2.236068
[4]> *a*
2.236068
[5]> (multiple-value-bind (a b) (polar 1 2) (list a b))
(2.236068 1.1071488)
[6]> 
Zubok ★★★★★
()
Ответ на: комментарий от torvn77

Ты не можешь заранее знать данные каких типов, каких размеров и в каком количестве ты будешь передавать таким способом.

Полная чушь. Могу знать, и знаю. В примере выше все типы и их количество описаны в сигнатуре функции.

Если же попытаться это сделать то тебе несомненно потребуется выделять участок памяти куда все эти данные складывать определённым образом…

А для возврата одного большого объекта, не влезающего в регистр, память выделять не требуется?

Стоп, а это тебе случаем ничего не напоминает?

Напоминает: ты опять пришел сюда позориться.

Siborgium ★★★★★
()

Forth же-ж. Вот он точно умеет возвращать tuple не маскируясь под структуры.

что и сколько в стеке положилось, то и результат

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Так там и не функции. Слова, слова, слова. (с) Прынц

anonymous
()
Ответ на: комментарий от anonymous-angler

Ура, узнал что то новое сегодня, просто это более короткая запись того что всегда делал

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от anonymous-angler

Хах:

<source>: In function 'add':
<source>:11:12: error: expected expression before '{' token
   11 |     return {
      |            ^
Compiler returned: 1

Си не умеет выводить типы, потому тип структуры-константы нужно писать явным приведением.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Уже года 4 не писал на C или C++. Пардон. Тем не менее g++ в первой попавшейся онлайн песочнице схавал и не подавился.

anonymous-angler ★☆
()
Ответ на: комментарий от anonymous

В SBCL, когда возвращается несколько значений, первые три возвращаются через регистры, а потом - через стек. Это на x86-64, резумеется. Разные реализации по-разному будут делать. По сути своей values - это больше для оптимизации:

* (defun test () (values 1.1 1.2 'a 123))
TEST
* (disassemble 'test)
; disassembly for TEST
; Size: 51 bytes. Origin: #x52C29447                          ; TEST
; 47:       488B15D2FFFFFF   MOV RDX, [RIP-46]                ; 1.1
; 4E:       488B3DC3FFFFFF   MOV RDI, [RIP-61]                ; 1.2
; 55:       488B35B4FFFFFF   MOV RSI, [RIP-76]                ; 'A
; 5C:       48C745F0F6000000 MOV QWORD PTR [RBP-16], 246      ; 123
; 64:       488D5D10         LEA RBX, [RBP+16]
; 68:       B908000000       MOV ECX, 8
; 6D:       F9               STC
; 6E:       488D65F0         LEA RSP, [RBP-16]
; 72:       488B6D00         MOV RBP, [RBP]
; 76:       FF73F8           PUSH QWORD PTR [RBX-8]
; 79:       C3               RET
NIL

* (defun test () (values 1.1 1.2 'a))    
WARNING: redefining COMMON-LISP-USER::TEST in DEFUN
TEST
* (disassemble 'test)
; disassembly for TEST
; Size: 36 bytes. Origin: #x52C29506                          ; TEST
; 06:       488B15D3FFFFFF   MOV RDX, [RIP-45]                ; 1.1
; 0D:       488B3DC4FFFFFF   MOV RDI, [RIP-60]                ; 1.2
; 14:       488B35B5FFFFFF   MOV RSI, [RIP-75]                ; 'A
; 1B:       488D5D10         LEA RBX, [RBP+16]
; 1F:       B906000000       MOV ECX, 6
; 24:       F9               STC
; 25:       488BE5           MOV RSP, RBP
; 28:       5D               POP RBP
; 29:       C3               RET
NIL
* 
Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

О, вот подобные случаи меня интересовали. RSI, RDI и RDX, выходит? А ведь они также используются в конвенции для передачи первых трех параметров функции

First Argument: RDI
Second Argument: RSI
Third Argument: RDX
Fourth Argument: RCX
Fifth Argument: R8
Sixth Argument: R9

Интересно, а функция с одним возвращаемым значением, вернет его через RAX?

x86-
() автор топика
Ответ на: комментарий от x86-

Интересно, а функция с одним возвращаемым значением, вернет его через RAX?

вернет так, как задумал тот, кто писал генерацию кода.

а он задумает так, как требуют всякие соглашения о совместимости.

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

alysnix ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.