Это же обычная функция, вроде, не имеющая состояния. Вообще, логично было бы сделать этот объект с состоянием, ведь этот элемент присутствует на схеме. Например, провода они делают с состоянием. Где логика? Далее, зачем они определяют процедуру invert-input прямо внутри вызова inverter? Ведь она же будет заново создаваться при каждом вызове! Ее нужно было бы вынести за пределы. Я что-то не понял, то ли я не врубился, то ли авторы делают буквально детские ошибки как в проектировании, так и в реализации. Неужели такое возможно? Что за на:)
Далее, зачем они определяют процедуру invert-input прямо внутри вызова inverter? Ведь она же будет заново создаваться при каждом вызове!
Ну она как бы на input замыкается, так что смысла выносить её за пределы функции нет. Можно было бы заменить её на обычную лямбду, но тут уже дело вкуса.
Вообще, логично было бы сделать этот объект с состоянием, ведь этот элемент присутствует на схеме. Например, провода они делают с состоянием.
Зачем? Элементы не имеют какого-то (наблюдаемого) состояния. Их как раз можно рассматривать как функцию с аргументами (входные провода) и возвращаемым значением (комбинация выходных проводов). В отличие от проводов, по которым в любой момент времени а) течёт ток; б) не течёт ток.
Сейчас на курсере, кстати, идёт курс nand2tetris, там вкратце рассматривается принцип работ логических элементов, если он тебе непонятен.
function inverter(input, output)
local function invert_input()
local new_value = not get_signal(input)
after_delay(inverter_delay, function ()
set_signal(output, new_value)
end)
end
add_action(input, invert_input)
return "ok"
end
function inverter(input, output)
add_action(input, function ()
local new_value = not get_signal(input)
after_delay(inverter_delay, function ()
set_signal(output, new_value)
end)
end)
return "ok"
end
function inverter(input, output)
input:add_action(function ()
local new_value = not input:get_signal()
after_delay(inverter_delay, function ()
output:set_signal(new_value)
end)
end)
return "ok"
end
Вроде все то же самое, а как читается по-разному, имо. Со второй лямбдой в лиспе был бы совсем кошмар.
ps. и самопальный синтаксис:
function inverter(input, output)
input:add_action(\{
local new_value = not input:get_signal()
after_delay(inverter_delay, \{ output:set_signal(new_value) })
})
return "ok"
end
Ну она как бы на input замыкается, так что смысла выносить её за пределы функции нет.
по ходу, наоборот, именно потому что в схеме нет динамического связывания, там начался бы ад передачи аргументов. То есть смысл то есть, но тогда это все выглядело бы окончательно по-уродски. Видимо, из эстэтических соображений так сделали, в ущерб здравому смыслу.
Зачем?
Просто так легче думать, чтобы не путать отображение с состоянием. К тому же, если бы мы переносили это например, на гуй, мы могли бы взять все компоненты as is и перенести на монитор, а так — придется извращаться. Короче, не слишком красиво это, ИМХО. Компонент должен быть компонентом, глаза режет от этого несоответствия. Потом могут возникнуть проблемы масштабирования.
После возврата из рекурсивного вызова power-iter выполняется ещё и умножение результата на a → значит вызов не хвостовой (необходимое условие для хвостовой рекурсии — чтобы рекурсивный вызов был последней операцией, которая выполняется в функции).
(define power-iter (lambda (a n acc) (if (= n 0) acc (power-iter a (- n 1) (* a acc)))))
Сделай трассировку ((trace power-iter)) и сравни результаты для обоих вариантов.
Нет, я уважаю Ваше мнение, знаю, что Вы действительно разбираетесь в программировании, просто вот я так думаю:). Из-за такого подхода, мне кажется, код полон тавтологий. Например, провод у нас имеет состояние, если бы инвертор был связан с конкретными проводами, нам не понадобилось бы эти обвязки, можно было бы писать просто что то типа insertor.invert. Я когда разберусь полностью с этой главой, постараюсь реализовать это по-другому, в pure-OOP стиле, поглядим что получится:)
У тебя там получается, что твой процесс итеративен, но не хвосто-рекурсивен. Это оптимизация вручную. А хвостовую рекурсию разворачивает сам компилятор.
а че там проверять? это и так видно. Возможно конечно, что компилятор это оптимизирует, но задача учебника показывать основы, а не финты ушами со стороны компилятора.
заметь что на половину его вопросов у уважаемых программистов из /development обычно нету ответа. Когда начинается обсуждение, у половины случается обострение Даннинга-Крюгера, с комментариями типа «ты идиот». А когда те же вопросы им будут задавать на собеседовании, все они обосрутся, потому что никогда не задавались такими вопросами
олсо, совершенно непонятно, как посетители Толксов куда ты переместил этот топик (которые не все программисты) смогут ответить на вопрос о деталях реализации sbcl и lispworcs. Таких людей три с половиной штуки, например, Лавсан и АлексОтт
вот ты знаешь, как оптимизируется этот случай?
(у меня есть ряд совершенно таких же вопросов по java, но они не задаются, просто потому что как деть ясно, что в /development никто не знает на них ответа :()
— Дональд Кнут, статья «Structured Programming with go to Statements» в сборнике «Computing Surveys» (Vol. 6, № 4, декабрь 1974, стр. 268).
Я просто уверен, что сам ты не пытался провести элементарные перфоманс тесты этого куска. И если я тебе сейчас скажу, что ты не прав, и видно что оно оптимизируется, тебе даже ответить-то будет нечего. Позорище :)
P.S. Найти в результате жизнедеятельности анонiмуса вопрос о деталях реализации lispworcs (что это такое, кстати?) - знак мастерства. Хорошо, что я не снес топик.
это http://www.lispworks.com/ напечатанный с опечаткой) почти стандарт в лисповском мире. Но это платная проприетарщина, для некоммерческой(!) разработки стоит больше косаря евро, для коммерческой начиная с 4 косарей евро, так что непонятно стоит ли зашквариваться об такой ад.. но лисперы едят и нахваливают
Либо я чего-то упускаю, либо вы оба наркоманы. Чему там оптимизироваться, если иннер функция захватывает контекст. Алсо, «создание функции» это не какой-то сложный механизм, а ни что иное, как выделение структуры с двумя членами — собственно указателя на функцию и массива захваченных переменных, обычный конс и не более (без всяких оверпарсингов овер-read'ов). Если бы тут был проброс сквозь еще один уровень вложенности, то можно было бы порассуждать, какую схему захвата использует реализация — флаттенинг, иерархию контекстов, или вундервафлю местного разлива — но его тут тупо нет. Нет никакого оверхеда, потому что альтернатива — создать контекст самому и таскать его руками рядом с функцией, пока та не помрет.
тебе, как знающему луа должно быть понятно различие. И тем более странно, что именно ты начал возражать. Да, это не слишком важно с точки зрения оптимизации, но это уродливый, чрезжопный подход, который хардкорит код там где не нужно, да еще и в ущерб перформансу.
А то что в схеме функции не могут захватить динамический контекст, это опять же проблема их недоязычка, он, же, один из авторов, Сассман, его и создал. Если ЯП у него кривой, это не значит, что надо студентам втирать говнопаттерны, пусть страдает, пробрасывает все явными параметрами. А то ведь хотят и рыбку съесть, и на х сесть.
В этом случае ты явно создаешь контекст-инвертер и таскаешь его, хотя внутри setInput() дело облегчается неявным селфом. Маллок так или иначе происходит, кроме случаев, когда конпелятор так крут, что может выоптимизить объекты на стек, но я в это не верю (а если так, то ему ничего не стоит и замыкания лифтануть, верно?). В оригинале же инвертер не имеет связанного контекста, и он добавляется по мере необходимости в сам input (add_action) и ранлуп (after_delay). Таким образом, оригинал аналогичен первому, а не второму коду из последнего коммента. К тому же, если посмотреть внутрь method, то там асинхронка after_delay, и инвертер также захватится на это время, причем целиком, а не только в части output, хотя я наврал про «их тут тупо нет», output захвачен сквозь invert(), поэтому равнозначно. Ты просто превратил два замыкания внутри халявной функции в обьект-контекст с методом и одно замыкание над ним, ничего принципиально не поменялось.
Боюсь у данной темы слишком маленькая аудитория, чтобы можно было определять, что есть дурной тон, а что «мейнстрим и бест практис». Так что это вкусовщина.
Кстати забыл сказать, луа с 5.2 не пересоздает функции, которые ничего не зохватили, т.е. умеет не создавать пустые замыкания, поэтому их можно писать там, где тебе надо, не засирая верхние скопы ненужными хелперами.