Ленивые вычисления
May. 23rd, 2010 12:33 amЧитаю статью про F# в очередном номере ПФП, а там стандартный набор бреда про сабж:
В противоположность жадному подходу существует стратегия ленивых вычислений, которая позволяет вычислять значение выражения только тогда, когда оно становится необходимо. Преимуществами такого подхода являются:
• производительность, поскольку неиспользуемые значения просто не вычисляются;
• возможность работать с бесконечными или очень большими последовательностями, так как они никогда не загружаются в память полностью;
• декларативность кода. Использование ленивых вычислений избавляет программиста от необходимости следить за порядком вычислений, что делает код проще.
По пунктам:
1.
а) Что-то я не припомню, чтобы программисты на неленивых языках стали бы вычислять какие-то значения, которые потом не используют. Обычно все-таки люди достаточно разумны, чтобы их программы вычисляли ровно столько, сколько нужно.
б) Ленивость создает накладные расходы, причем существенные. Если две программы делают одно и то же, ленивый вариант никак не будет производительнее энергичного. Почему-то большинство проблем с производительностью программ на Хаскеле решают именно расстановкой всяких ! и $!, убирающих ленивость. И оптимизатор тем же занимается.
2.
С бесконечными структурами ни ленивые, ни энергичные программы работать не могут, они всегда работают с конечной их частью. Насчет больших последовательностей - в неленивых языках они доступны в виде итераторов, энуменаторов и т.п., но это несколько другая вещь, т.к. значения обрабатываются по очереди и не хранятся в памяти, в отличие от ленивых списков и структур. Если же что-то в памяти хранить, то из-за накладных расходов на ленивость ленивые языки принципиально могут хранить меньше данных в тех же объемах памяти, чем неленивые.
3.
От необходимости следить за порядком вычислений избавляет исключительно чистота, а не ленивость. Если код не весь чист (содержит побочные эффекты), то ровно наоборот - ленивость заставляет думать о порядке вычислений гораздо больше обычного, т.к. программа ведет себя не так, как обычно подсказывает интуиция. В строгих же языках порядок вычислений настолько прост и понятен, что задумываться не заставляет.
В противоположность жадному подходу существует стратегия ленивых вычислений, которая позволяет вычислять значение выражения только тогда, когда оно становится необходимо. Преимуществами такого подхода являются:
• производительность, поскольку неиспользуемые значения просто не вычисляются;
• возможность работать с бесконечными или очень большими последовательностями, так как они никогда не загружаются в память полностью;
• декларативность кода. Использование ленивых вычислений избавляет программиста от необходимости следить за порядком вычислений, что делает код проще.
По пунктам:
1.
а) Что-то я не припомню, чтобы программисты на неленивых языках стали бы вычислять какие-то значения, которые потом не используют. Обычно все-таки люди достаточно разумны, чтобы их программы вычисляли ровно столько, сколько нужно.
б) Ленивость создает накладные расходы, причем существенные. Если две программы делают одно и то же, ленивый вариант никак не будет производительнее энергичного. Почему-то большинство проблем с производительностью программ на Хаскеле решают именно расстановкой всяких ! и $!, убирающих ленивость. И оптимизатор тем же занимается.
2.
С бесконечными структурами ни ленивые, ни энергичные программы работать не могут, они всегда работают с конечной их частью. Насчет больших последовательностей - в неленивых языках они доступны в виде итераторов, энуменаторов и т.п., но это несколько другая вещь, т.к. значения обрабатываются по очереди и не хранятся в памяти, в отличие от ленивых списков и структур. Если же что-то в памяти хранить, то из-за накладных расходов на ленивость ленивые языки принципиально могут хранить меньше данных в тех же объемах памяти, чем неленивые.
3.
От необходимости следить за порядком вычислений избавляет исключительно чистота, а не ленивость. Если код не весь чист (содержит побочные эффекты), то ровно наоборот - ленивость заставляет думать о порядке вычислений гораздо больше обычного, т.к. программа ведет себя не так, как обычно подсказывает интуиция. В строгих же языках порядок вычислений настолько прост и понятен, что задумываться не заставляет.
no subject
Date: 2010-05-26 12:17 pm (UTC)Я делал попытку "посмотреть" на LISP, лет 5 назад. Прочитал полторы книги, поставил на свой Windows какой-то унылый OpenSource вариант и запустил "Hello, World". Не доставило.
Да, C++ нормальный язык, который покрывает очень широкий спектр возможных уровней абстракции. Полгода назад я бросил его использовать каждый день перейдя на C#, до этого использовал его каждый день года с 2000, и в общем-то не против продолжить и в будущем.
На LISP можно написать device driver? Прошивку для контроллера в какой-нить железке? System service? Native Win32 GUI application? Современную realtime-игру? Web application? Realtime network server (e.g. billing, trading)? Насколько я понимаю, LISP умеет только последние 2 пункта. Конечно если бы Windows был написан на LISP а процессоры исполняли LISP-код, всё было бы наоборот.
Думаю качества С++ которые ты охарактеризовал как "набор костылей" в какой-то мере неизбежны для языка, позволяющего одновременно программировать на assembler, и использовать очень высокоуровневые абстракции.
no subject
Date: 2010-05-26 12:33 pm (UTC)Насчет системных сервисов - почему-бы не писать их на лиспе? я вполне успешно запускаю жабовский код в виде сервисов, и все работает. GUI - можно и GUI сделать, я видел биндинги к DirectX, так что это не проблема.
А насчет С++ - вот оттого, что он пытается совместить плохо совмещаемые концепции, то и возникают проблемы. Необходимо использовать наборы разных языков, каждый из которых хорошо решает свою задачу, а не пытаться сделать один язык, который умеет все.
no subject
Date: 2010-05-26 01:45 pm (UTC)У линукса и так фатальная проблема - отсутствие бинарной совместимости программ между дистрибутивами.
>почему-бы не писать их на лиспе?
>я вполне успешно запускаю жабовский код в виде сервисов, и все работает.
Потому что сервис не то же самое, что приложение командной строки, запускающееся с правами system на отдельном desktop после включения компьютера.
Во-первых ему обычно надо как-то взаимодействовать с остальным миром. Например протокол DCOM довольно популярен для IPC с сервисами, из-за интеграции в него windows security: мало кто хочет разрешить "everyone" trustee посылать даже 1 байт в работающий с правами localsystem сервис, что неизбежно при использовании более общепринятых IPC вроде sockets которые наверняка есть в LISP.
Во-вторых сервисы часто хотят реагировать на десятки событий: power states, изменение набора сетевых карт, login/logout пользователя, etc.
>можно и GUI сделать, я видел биндинги
Технически - можно.
Практически - можно только "hello world", и может быть вращающийся чайник ещё получится.
Сложный GUI намного удобнее делать в IDE для этого, а именно в MS Expression Studio, MSDEV, Delphi, или на худой конец Eclipse + QT designer. Причём степень этого "намного" возрастает экспоненциально с усложнением GUI, а может это сложность GUI экспоненциально зависит от относительно субъективной оценки "удобный и красивый". Причём в результате конкуренции + технического прогресса, субъективная оценка "удобный и красивый" возрастает линейно от времени: сравни например внешний вид word 6.0, 2000, и 2007. IMO эта асимптотика уже привела к тому, что качественный и красивый современный GUI невозможно сделать обладая только байндингами, без поддержки со стороны IDE + дизайнера в штате или на аутсорсинге.
Пример GUI на LISP.
Пример современного GUI.
Про инструментальные средства разработки игр я уж и не заикаюсь, один AlienBrain чего стоит.
>использовать наборы разных языков, каждый из которых хорошо решает свою задачу
Согласен с оговорками.
Например ты очень долго будешь кодить и отлаживать механизмы взаимодействия VBScript кода и LISP кода, из-за слишком разного всего.
В частности поэтому MS придумали платформу .NET, в рамках которой можно программировать на чём больше нравится из довольно широкого набора C++/C#/VB.NET/Iron Python/IronRuby/F#, не испытывая особых затруднений из-за необходимости взаимодействовать с кодом на других языках.
no subject
Date: 2010-05-26 01:59 pm (UTC)насчет платформы .Нет - да, платформа относительно хорошая, и я надеюсь, что для нее доделают Clojure, которая почти Лисп (не Common), но она высокоуровневая. Так что придется использовать какой-то низкоуровневый язык для драйверов и т.п.
P.S. насчет отстутствия бинарной совместимости - это не совсем так, это скорее ситуация с dll hell в винде. Кому надо, нормально собирает бинарник, который работает на всех платформах