LINUX.ORG.RU

кортежи не нужны?

 


0

1

Вот мне всегда казалось, что кортежи - это лишнее понятие. Сейчас копаюсь в потрохах SBCL и вижу, насколько они усложняют вывод типов и систему типов. Скажем так, более чем вдвое. В Си можно можно просто определить struct. Можно его инициализировать двумя значениями в красивой форме

struct gasket {
    float weight;
    unsigned height;
    unsigned diameter;
};
 
void main() {
    struct gasket obj = { 12.f, 120, 30 };

Можно вернуть структуру из функции, можно передать в функцию. Спрашивается, а зачем тогда кортежи? Что нового они вообще дают хоть в каком-то языке? Понятно, что в языках, где список может состоять из элементов только одного типа, и где нет типа any/t/variant, кортежи могут быть кривым костылём для полиморфных списков. Ну и теоретически они могут быть эффективнее. Но Си всё равно даёт то же самое, плюс поля кортежа ещё и именованные. Единственное неудобство - это то, что тип структуры нужно объявлять. Но в этом можно видеть плюс, т.к. объявление структуры заставляет дать вещам имена и тем самым сделать программу более явной и понятной. А типизация полей структуры убережёт от ошибок.

Единственное, что я заметил - так это то, что времена жизни, константность и прочие атрибуты объектов в кортеже никак не связаны, а в структуре - связаны. Но если структура наполнена указателями, то и это почти не имеет значения.

Что я упустил?

★★★★★

Последнее исправление: den73 (всего исправлений: 3)

Ответ на: комментарий от monk

Мало кто заметил, но аналогичную задачу я решил для лиспа Правда, о стоимости пока ничего хорошего сказать нельзя - оно тянет на «контракты», проверяемые во время выполнения.

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

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

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

пусть q = floor(3, 2).quotient

А если нужны оба, тогда вместо

пусть q, r = floor(3, 2)

получаем

пусть tmp = floor(3, 2)
пусть q = tmp.quotient
пусть r = tmp.remainder

многословно...

Список возвращать плохо из-за накладных расходов на сам список. Тогда уж массив.

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

Но ничто не мешает просто вернуть массив, не называя его отдельным словом «кортеж».

У массива нельзя ограничить типы элементов. Хотя если типы позволяют описать массив, у которого ровно 3 элемента, первый имеет тип число, второй — строка, а третий — булево, то такой массив ничем от кортежа отличаться не будет.

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

многословно...

Да, поэтому можно рассмотреть отдельно операцию распаковки массива в переменные, впрочем это сахар, не являющийся жизненно важным. Т.е. он не оправдывает существования такой отдельной сущности, как кортежи. И главное, он не привязан к кортежам как таковым. Можно и другие стр-ры данных так распаковывать.

оптимизатор должен выкидывать создание списка.

В XXIV веке, наверное, так и будет.

У массива нельзя ограничить типы элементов.

Если тип каждого элемента известен, то это структура. Если нет, то это массив.

Хотя если типы позволяют описать массив, у которого ровно 3 элемента,

Позволяют, через satisfies, но вряд ли от этого будет много пользы.

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

В лиспе это делается так:

(rotatef a b)

В Яре - никак. Распаковка пока что осталась за бортом парохода. Хотя можно проимпортировать rotatef и будет как-то так:

поменять-местами(a, b)

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

У тебя массивы в лиспе имеют оператор сравнения? А что на счет эффективности? Что на счет типобезопасности (это если оператор сравнения массивов есть)?

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

Коротко говоря, Яр в идеале - это Common Lisp с синтаксисом примерно как у Питона и с исправленными (в моём понимании) ошибками дизайна CL.

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

В лиспе бывают типизированные и нетипизированные массивы. Оператор сравнения можно определить свой, назвав его, скажем, v<, v<=, v== . Если очень нужно, то можно и перешибить универсальные операции <,>. Правда не очень получится перешибить системный оператор сравнения произвольных графов, включащих разнородные типы данных, но можно написать свой.

Поскольку в лиспе нет инфиксного синтаксиса, то боль от его отсутствия (или радость от его отсутствия) всегда с тобой. Нет разницы между «встроенным оператором» и «твоим собственным оператором».

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

Типобезопасность для типизированных массивов есть. Для нетипизированных - нет. Для максимальной эффективности пишешь (safety 0)(speed 3) и получаешь при ошибке типа undefined consequences. Компилятор НЕ сможет тебя предупредить о многих проблемах, т.е. это где-то как Си с массивными void *. Мы с Монком работаем над патчем компилятора, чтобы улучшить ситуацию, но вряд ли этот патч будет кому-то интересен, поэтому он будет в Яре и может быть в виде библиотечки.

Если пишешь safety > 0, то SBCL выкинет те проверки типов, которые он сможет доказать статически, а остальные оставит в рантайме. В рамках одной функции можно смешивать разные уровни safety, так что можно управлять компромиссами.

В целом SBCL медленнее Си раза в три согласно computer benchmark game, что на мой взгляд весьма недурно. Если использовать генерацию кода в рантайме, которая в computer benchmark game не приветствуется, результаты лиспа будут лучше.

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

Хм, окей. Сколько нужно кода, чтобы решить задачу описанную мной. Сформулирую ее еще раз и дополню:

1) Есть структура Name: string; department_id: int;

2) Есть массив этих структур

3.1) Надо отсортировать этот массив по двум параметрам, сначала по department_id, а если он эквивалентент, то по имени.

3.2) Надо отсортировать массив только по имени.

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

Как-то так (не тестировал и скобки мог неверно посчитать, что в мире лиспа является нормой :) )


(defstruct struct
 (name "" :type string)
 (department_id 0 :type integer))

(declaim (ftype (function (struct struct) t)) struct<)

(defun struct< (a b) "Сортировка по dept_id, потом по name"
 (or 
   (< (struct-department_id a) (struct-department_id b))
   (and (= (struct-department_id a) (struct-department_id b))
        (string< (struct-name a) (struct-name b)))))

;; это не массив структур, а массив 
(let ((my-array (make-array '(2) :element-type struct 
                 :initial-contents
                 (list ; да, это коряво, а иначе руками заполняй
                  (make-struct :department_id 1 ...)
                  (make-struct ...)))))
  (sort my-array #'struct<))

; 3.2. предоставляется читателю (нужно написать другую ф-ю)

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

А в го нет кортежей. Есть просто функции, которые могут возвращать несколько значений, но вот чтобы кортежи.. нет, в го их нет.

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

На с++


struct SPerson
{
  std::string name;
  int department_id;
};

...
std::vector<SPerson> v;
...
std::sort(v.begin(), v.end(), [](const auto& f, const auto& s)
 {return std::tie(f.department_id, f.name) < std::tie(s.department_id, s.name);});
...
std::sort(v.begin(), v.end(), [](const auto& f, const auto& s)
 {return std::tie(f.name) < std::tie(s.name);});

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

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

v.erase(std::remove_if(v.begin(), v.end(), [](const auto& v)
 { return std::tie(v.name, v.department_id) == std::make_tuple("a", 1); }));

Заметь, нам тут не надо создавать временную структуру, что является плюсом. Допустим у нас в структуре есть {int a; int b; string c; string d;}; А мы хотим сделать удаление только по a и c, а в другом месте проги по c и d. Нам нет необходимости писать дополнительные операторы или функторы, которые знают сравнивают структуру. Все делается очень красиво и лаконично через пакинг в туплы.

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

Кстати в случае с кортежами ты избавляешься от создание референсной структуры, что позволяет получить одно, несколько, или все следующие приемущества:

1) В случае если ты хочешь сделать сортировку/удаление по нескольким полям, но не по всем полям структуры, то код с кортежами локаничнее

2) Он эффективнее кода с массивами произволных типов, ты (точнее алгоритмы) просто обращаешься к полям тупла, а не итерируешь такие массивы.

3) Теюе не надо инициализировать все поля структыры которые тебе не нужны, что а) локаничнее б) эффективнее

4) Если ты используешь язык с конструкторами и деструкторами (не знаю твой яр такой?), то ты избавляешься от сайд эффектов (и оверхэда) вызова конструктора и деструктора референсной структуры/класса (если у нее есть такие).

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

Ну так, определённый сахар, но можно и без него. Например, в лиспе сочетание из tie и < можно было бы сделать макросом

(defmacro compare-tuples (x y)
 ...)

(compare-tuples ((struct-department_id a) (struct-name a))
                ((struct-department_id b) (sturct-name b)))
И получить по-сути то же самое (это именно кортежи - они не являются структурами данных, а существуют лишь как элементы синтаксиса). В Яре так, правда, нельзя сделать. Но макросы, может быть, в Яр будут когда-то добавлены. Соответственно, если вдруг они не будут добавлены, можно будет вернуться к вопросу о кортежах.

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

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

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

Тот же уровень комфорта означает наличие в языке/стандартной либе лаконичного и комфортного типового (универсального) решения?

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

Макрос пишется примерно так для случая тюплов длиной два:

(defmacro tuple< (x y)
  (with-gensyms (x1 x2 y1 y2)
    `(let ((,x1 ,(first x))
           (,x2 ,(second x))
           (,y1 ,(first y))
           (,y2 ,(second y)))
       (or (< ,x1 ,y1)
           (and (= ,x1 ,y1)
                (< ,x2 ,y2))))))
;и тогда компилятор развернёт 
(tuple< (a b) (c d)) 
в подобие 
(or (< a b) (and (= a b) (< c d)))
Тут единственная проблема, что < в лиспе не расширяемое, но ты можешь ввести в своей программе другое, своё <, которое будет расширяемым. Хотя этим никто обычно не парится и просто передают < и = как параметры макроса.

ДЛя случая длины N он тоже пишется за полчаса-час, просто будет более непонятным. Но N должно быть константой и тогда никаких накладных расходов на это не будет. Если нужно N, известное только в рантайме, то это уже нужны функции.

Т.е. при фиксированном N никакой стр-ры не создаётся. При этом я всё это пишу слёту, и любой программист на лиспе мог такое писать слёту в 1987 году. А разработчикам С++ понадобились на это десятилетия (хотя это я уже начинаю переходить на тему своей ненависти к С++, что вряд ли продуктивно).

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

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

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

Конкретно для твоей задачи я бы решил её тем способом, к-рый я привёл с самого начала. Это достаточно быстро пишется и без большого труда, причём полная свобода изменений. Понадобился другой предикат для сравнения полей - пожалуйста - напиши его и пользуйся. Сама по себе идея, что контейнер данных имеет раз и навсегда заданные операции сравнения, лиспу не свойственна, поэтому проблемы сделать что-то с другой операцией нет.

den73 ★★★★★
() автор топика

Кортежи нужны, если нужно вернуть несколько значений откуда-нибудь. Создавать структуру заранее на каждый чих --- будет неудобно. Однако, этот аргумент не сработает, если Вы поддерживаете анонимные структуры в языке тем или иным образом.

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

Не обязательно анонимные, можно и локальные. Т.е. понятно, что анонимные на момент написания кода удобнее. А вот когда потом в стеке всё вокруг анонимное - за это удобство наступает жесткая расплата.

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

Вообще это синтаксис для compare-tuples как раз говорит, что кортежи для этого случая нужны. Блин.

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

Все равно, получается ты пишешь кучу кода. Правильно ли я понимаю, что если выборка идет по 2 полям - одна функция сравнения, по трем - другая, по 3 - третья и т.д.? Но это ж говнокодом попахивает и анти-инженерией.

И главный вопрос: зачем писать второй лисп? Он же есть уже.

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

Понадобился другой предикат для сравнения полей - пожалуйста - напиши его и пользуйся.

Так в том и дело, что в с++ его писать не надо. Не надо писать кучу ифов если тебе надо сравнить 10 полей. Все реализовано! Сделай пакинг и передай в стандартную функцию. И все.

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

Ну по сути тупл может быть заменен или структурой, или массивом из рефлекторных типов. Это да. Но лишь частично. По сути тупл(кортеж) сочетает в себе плюсы обоих, для определенного набора задач разумеется:

- Возможность написать универсальный код для упаковки (присовоение нескольких выходных знач, сортировки, удаление). Собственно это позволяет сделать и рефлекторный массив. Но без типобезопасности и эффективности.

- Типобезопасность и эффективность может дать нам структура. Но она не дает универсальности в написание алгоритмов этого класса.

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

Проблема лиспа в том, что его нельзя улучшить из-за возраста, а время идёт и конкуренты подрастают. Плюс эзотерический синтаксис. Разницу см. здесь

Написание прослойки вполне решает задачу придания лиспу возможностей для роста.

Правильно ли я понимаю, что если выборка идет по 2 полям - одна функция сравнения, по трем - другая, по 3 - третья и т.д.?

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

Не надо писать кучу ифов если тебе надо сравнить 10 полей.

Так эти ифы пишутся в норме один раз для макроса с эн полями и потом всю жизнь пользуйся. Макроса такого у меня нет, а функция такая была (продолбал куда-то, пришлось новую писать и опять продолбал). В стандартной библиотеке такого нет (или я просто не знаю, как его назвать). На самом деле этот способ сравнения будет работать лишь тогда, когда тебя устраивает определение < для всех типов данных, к-рые могут присутствовать в полях. А понадобилось тебе сравнить строки без учёта регистра - и приехали.

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

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

Предикату в этом случае надо выдумать корректное имя=) А не PredForCompPerson1(2,3,4) :D

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

Вопрос такой - можно ли тупл передать как параметр в функцию?

Конечно. Но именно в с++ тупл само собой имеет фиксированный размер и фиксированый набор типов в определенном порядке.

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

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

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

Я кстати, вспомнил, что я делал туплы в виде таких костылей для Firebird - там записей нет, а я их таким способом реализовал. Но там записей нет, поэтому туго было, а реализовать иначе чем через эн параметров было просто невозможно.

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

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

Именно в с++ тупл - это шаблонная структура, то есть передается она как обычная структура. С большой долей вероятности компилятор просто преобразует все обращения внутри вызываемой функции в

base_addr+field_offset

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

А понадобилось тебе сравнить строки без учёта регистра - и приехали.

  std::tie(to_upper(f.name)) < std::tie(to_upper(s.name));

Правда к сожалению в стандартной либе to_upper нет, но можно написать или свой костыль на пару строк(один раз и навсегда), или заюзать буст.

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

Или даже в общем случае так.

  bool operator<(TempT& f, TempT& s);
  
  std::tie(toTempT(f.name))...

Это конечно тоже уже костылек. Но без него и в лиспе никуда=)

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

Хотя в этом и предыдущем надо скорее юзать std::make_tuple, ибо тут у нас ссылка на временый объект получается.

То есть что-то вроде.

std::make_tuple(to_upper(f.name), f.department_id)

Но сильно основной идеи это не меняет.

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

Структуры, объявленные локально внутри функции или другой области видимости.

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

На с++

Ну для этого в лиспах тюплы не надо

;; это пишется один раз
(defun struct< (a b accessors)
  (cond
    ((null accessors) nil)
    ((let ((accessor (car accessors))
           (gen< #'<))
       (when (consp accessor)
         (setf gen< (cdr accessor)
               accessor (car accessor)))
       (funcall gen< (funcall accessor a) (funcall accessor b))
     t)
    (t (struct< a b (cdr accessors)))))

(defun make-struct< (accessors)
  (lambda (a b) (struct< a b accessors)))

И вот полный аналог C++

(let ((v (make-array ...)))
  (sort v (make-struct< (list #'struct-department_id (cons #'struct-department_name #"string<))))
  ...
  (sort v (make-struct< (list (cons #'struct-department_name #"string<)))))

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

Но без него и в лиспе никуда=)

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

Именно в с++ тупл - это шаблонная структура, то есть передается она как обычная структура.

А если её не надо передавать? Ты ж только что говорил, что ничего никуда перепаковывать не нужно? «Заметь, нам тут не надо создавать временную структуру, что является плюсом.» И я из этого заключил, что эл-ты тупла только синтаксически общие между собой, а на самом деле каждый из них - это отдельная переменная. Тогда возникает экономия на том, что не нужно двигать по стеку ссылки на эти поля. Проясни, а то я уж запутался.

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

Мне кажется, не особо круто то, что ты написал, т.к. лямбда генерируется при каждом вызове, и вряд ли компилятор это соптимизирует. Хотя я не на 100% уверен. Дальше, если говорить о скорости, то лямбды медленнее обычных функций (вроде бы). Если сделать везде полный inline, то есть мизерный шанс, что соптимизируется. Эти предикаты я бы сгенерировал каким-нибудь макросом def-struct<, а не make-struct<.

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

А если её не надо передавать?

Ты только что спросил как она передается, а теперь «если не надо передавать».

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

Перефразируй вопрос, я вообще ничего не понял.

Тупл в с++ (имено тут, ибо кортежи ядром не поддерживаются), это балонная структура. std::tie Создает структуру но через референсы, то есть ты не копируешь значения из изначальной, а только ссылки. Вторым аргументом (make_tuple) ты создаешь не целую рефернсную структуру со всеми полями, а только с нужными для операции.

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

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

В смысле? Она создаётся один раз на вызов sort также, как и в варианте на С++.

Дальше, если говорить о скорости, то лямбды медленнее обычных функций (вроде бы).

При правильной реализации нет. Лямбды транслируются в структуры один-в-один.

Эти предикаты я бы сгенерировал каким-нибудь макросом def-struct<, а не make-struct<.

И получил бы имя, которое используется в коде один раз и описано где-то в другом месте. Примерно как на C++ писали пока лямбды не придумали.

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

Ты только что спросил как она передается, а теперь «если не надо передавать».

Меня интересуют оба случая.

Но из того, что ты пишешь, выглядит не настолько круто, как я думал. Я имел в виду такое, что мы пишем tuple(a, b), а компилятор вообще не генерирует никакой структуры, а создаёт две переменных:

auto tuple_field_1 = a
auto string b

Ну и дальше оптимизатор выкидывает лишние переменные при хороших условиях и в итоге использует поля исходной структуры «in-place».

Хотя кто знает, что там оптимизатор делает. ВОзможно, он так и делает в итоге.

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

Она создаётся один раз на вызов sort также, как и в варианте на С++.

Ну в смысле, при каждом входе в sort. А можно её создать один раз на все входы в sort, ведь в жизни обычно код выполняется не один раз.

Лямбды транслируются в структуры один-в-один.

Я вообще-то мерял. Лямбда - это структура из двух полей - функция и данные. А функция - это просто указатель. Хотя не ручаюсь, что я померял на 100% точно, но так скажем, на 80% уверен.

И получил бы имя, которое используется в коде один раз и описано где-то в другом месте

Зато его можно из отладчика и трассировщика увидеть. Но если не нравится, есть macrolet.

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

А можно её создать один раз на все входы в sort, ведь в жизни обычно код выполняется не один раз.

Если стало важно время создания лямбды (по сути инициализации блока памяти на стеке, то есть пара ассемблерных инструкций), то надо писать на Си++. На фоне времени сортировки в любом случае пренебрежимо малая величина.

Я вообще-то мерял. Лямбда - это структура из двух полей - функция и данные. А функция - это просто указатель

Вызов функции от лямбды не отличается (только на стек дополнительный указатель пойдёт). Про инициализацию лямбды я чуть выше написал.

Вообще если дошло до такой микрооптимизации, то и multiple-values структурами или списками заменять нельзя. Ведь это лишняя аллокация и нагрузка на сборщик мусора.

Но если не нравится, есть macrolet.

Который развернётся в flet, который та же лямбда.

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

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

Ведь это лишняя аллокация и нагрузка на сборщик мусора.

Теоретически для этого есть dynamic-extent. Будет ли он работать между функциями (если я верну структуру вместо multiple-values) - вот вопрос интересный.

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