Зачем нужно функциональное программирование?
> кроме банального примера emacs-а.
Пишут. И защищают дисеры.
А то убожество с filter, которое ты смог написать на С, там выглядит очень естественно и понятно.
// shurick
ИМХО, дисер - это маленькая программа.
Но вот как происходит работа с "реальными объектами" (окна, файлы, события, время и т.д. совершенно не понятно.
поэтому иногда действительно достаточно написать очень мало.
Но на тот же С это за#$ёшься переписывать.
// shurick
Я не считаю целесообразным его использование там,
где понятно что и как надо делать (в т.ч. "вычисления").
Но, тем не менее, он очень уместен для моделирования,
построения прототипов и разработки алгоритмов некоторых
типов. А также для обучения (насчёт школьников не знаю,
но для студентов - вполне).
//shurick
> На функциональных языках, вообще, большие программы можно написать? Воообще, пишут?О! Именно, что защищают диссеры. А теперь покажи мне программный продукт платный или бесплатный на этих самых языках. Главное что бы продукт был используемый.
> кроме банального примера emacs-а.
Пишут. И защищают дисеры.
Лично я только одну программу, которой я пользуюсь, написанную на культурном языке, сходу могу назвать: mldonkey.
А теперь покажи мне программный продукт платный или бесплатный на этих самых языках.Рекомендую попрактиковаться с такой командой (под FreeBSD):
find /usr/ports -name Makefile | xargs grep -il token
где token это lang/ocaml, lang/scheme48 и т.п. Порты найденные в devel не считаем - это библиотеки и инструменты к этим языкам.
"Миллионы мух не могут ошибаться" (с)
Примерами програмных продуктов пока в основном служат компиляторы/интерпретаторы этих языков. Индустрия программирования игнорирует ФЯ по понятным причинам, в них некому вложить бабки для раскрутки.
а как там M$ кстати?
"Миллионы мух не могут ошибаться" (с)
Я не сказал "где большие корпорации пишущие софт на Lisp или OCaml?". Я не сказал "где сотни девелоперов?". Я сказал "где продукты?", пускай они будут написаны одни человеком. Я не вижу ничего кроме диссертаций.
Интересуются немодными языками в основном люди с научным складом ума, им дисеры интереснее.
"Продукт" одним человеком трудно сделать, как правило в таких случаях получается что-то,
что только автор может использовать.
Для продукта нужно в команде работать - соотвественно чем немоднее язык, тем людей меньше.
В Open Source совсем плохо с этим - ведь не ты ищешь помошников, а они тебя.
На ocaml есть несколько используемых программ, по-видимому это связано с тем,
что в некоторых французских ВУЗах заставляют его учить.
Что значит "пока"?
ФЯ одна из самых первых парадигм, тому же Лиспу лет, наверное, больше, чем языку С, а реальных программ, кроме того же emacs-а - так и не видно.
имхо, ФЯ удобно применять для генерации программ, но не для самих программ.
Т.к. фя очень тяжело ложится на mutable-объекты, на объекты-имеющие состояния и т.д.
Всякие преобразования/трансформации данных очень легко пишутся на ФЯ, но архитектура/структура программы на ФЯ пишется отвратно. Может быть имеет смысл связка UML + фя, где на выходе у UML-я некие объекты с которыми умеет работать уже ФЯ
Это относится только к чистым языкам, из которых здесь упоминался только Haskell.
По хорошему, нужна следующая связка:
1. сначала описываем объекты (что они из себя представляют, какие взаимосвязи между объектами есть и т.д.) - реального языка-кандидата на такое описание сейчас нет
2. На ФЯ описываем преобразования/трансформации данных
3. С помощью императивного языка все это связываем вместе.
Обращаю внимание, что именно императивный язык является связкой, т.к на нем проще писать сложные задачи.
Это сейчас императивные языки успешно борются за звание самых тормозных, а тогда все было иначе.
Архитектура и структура пишутся замечательно, просто думать надо по другому.
Lisp, Ocaml, Perl, Python - мультипарадигмальные языки:
поддерживают процедурное, функциональное программирование, ОО
Умеют соотв. всё что обычно нужно в рамках этих парадигм.
> 1. сначала описываем объекты (что они из себя представляют, какие взаимосвязи между объектами есть и т.д.)
Это относится к представлению знаний о предметной области скорее, а не к программированию.
> 2. На ФЯ описываем преобразования/трансформации данных
> 3. С помощью императивного языка все это связываем вместе.
Для бухгалтерии всякой, про которую тут в основном говорят, так и надо наверное.
Если например у нас событийно-ориентированная система, то этой обвязки будет - один event loop фактически.
Нечистые - это те, которые содержат нефункциональные фичи. Т.е. переменные в понимании ИЯ (в ФЯ переменных нет вообще, строго говоря исключения, нефункциональные функции типа ввода-вывода, короче все, что может дать косвенные эффекты при выполнении.
Возьмем стандартную задачу про чайник, который надо поставить на огонь. Как этот алгоритм будет записываться на ФЯ?
Напиши его на ИЯ, чтобы я понял, что ты имеешь ввиду.
> поддерживают процедурное, функциональное программирование, ОО
C++ тоже поддерживает функциональное программирование, но это не значит, что на нем можно эффективно писать используя ФЯ подход. Постоянно приходится обходить кучу грабель, которые разбросаны тут, и там.
Приведи, плиз, пример ОО на ФЯ, только лучше какой-нибудь красивый, чтобы в нем было проще разобраться.
> Это относится к представлению знаний о предметной области скорее, а не к программированию.
Согласен. Но ведь программа должна знать то, с чем она работает. Причем для эффективной работы должна знать как можно больше.
> Для бухгалтерии всякой, про которую тут в основном говорят, так и надо наверное.
> Если например у нас событийно-ориентированная система, то этой обвязки будет - один event loop фактически.
Чем бухгалтерия отличается от событийно-ориентированный системы?
{
ВзятьЧайник;
НалитьВодуВЧайник;
ПоставитьЧайникНаПлиту:
ВключитьОгонь;
ДождатьсяЗакипания;
ЗасыпатьВКружкуВодуЧайиСахарПоВкусу;
}
Я в ОО не рюхаю, но в OCaml говорят очень хорошее объектное расширение.
Отзывы примерно такие: этими объектами реально удобно пользоваться (имеется ввиду в отличии от яв и плюсов
но типа если язык изучить получше, окажется, что не так уж они и нужны, типа с модулями и функторами проще получается.
Примера кода не могу привести.
> Чем бухгалтерия отличается от событийно-ориентированный системы?
Не отличается
Об том и речь.
Представь, что он роботом управляет, он же всю комнату порушит или сожжёт
каждую строчку записать в виде "ОноЕсть?ДелатьТо-То:Выход"
Т.к. есть много разбросанных правил. И понять какое из них срабатывает в какой момент почти не реально для большой системы.
Еще как-то правильно заметили, что при добавление каждого нового правила надо перетестировать поведение всей программы, т.к. нельзя гарантировать, что добавление даже самого незначительного правила не убило все системы.
Как правильно заметил за каждой строчкой скрывается проверка предусловия и само выполнение действия.
Ты уверен, что ни с чем не путаешь?
Правила - скорее в логическом программировании.
Абсолютно неверное утверждение. Все ошибки в типизированном ФЯ носят логический характер. Если есть ошибка, значит ты где-то напутал с логикой программы. Поэтому искать их гораздо проще, чем в С++, скажем. Это я говорю из своего опыта.
Еще большое количество ошибок отлавливается за счет строгого контроля типов. + ошибки обычно генерируют исключения из-за неправильного pattern matching.
+ ФЯ, даже нечистые, очень неприветствуют side effects. Это к вопросу о "нельзя гарантировать, что добавление даже самого незначительного правила не убило все системы". Для ФЯ это неверно, поскольку подавляющее большинство функций не имеют стронних эффектов в принципе.
Вроде, нет.
Сначала мы описываем функции - фактически правила преобразования причем компьютер(компилятор) выбирает наиболее подходящий вариант, а также выбирает порядок выполнения
Добавление или изменение одного из базовых правил (например, по ошибке добавление стороннего эффекта) приводит к очень интересным последствиям.
Любое же взаимодействие с внешним миром (с другими программами, компонентами, пользователем, ОС, железом и т.д.) - это уже сторонние эффекты.
fun get_kettle = if kettle exists then Some kettle else None;
fun fill_kettle kettle = if water exists then Some (fill kettle) else None;
fun na_plitu kettle = if plita exists then Some (put kettle) else None;
....
val funcs = [fill_kettle, na_plitu, fire_it, wait_for_boil, do_other_shit];
fun evaluate list expr = match (expr,list) with
(Some e, h::t) => evaluate t (h e)
| (Some e, nill) => 1;
| (None, _) => 0;
fun main = let kettle = get_kettle in
if (evalute funcs kettle) then print "success" else print "zopa";
Это без исключений. С исключениями, соответственно, было проще.
а для кого всякие fold написали?
Даже на чисто функциональном языке программа будет императивной (как это делается, я не в курсе,
есть какая-то магия для этого).
Теперь рассмотрим более реалистичный вариант - управление роботом, с обратной связью в полный рост.
Задачу управляющей программы можно сформулировать в терминах
преобразования потока сигналов от датчиков в поток команд для манипуляторов.
Получается уже чистая функция, которую вполне нормально будет записать даже на чистом ФЯ.
Добавляешь императивный event loop - активная сущность, которая будет вытягивать из выходного потока
команды и вставлять во входной поток сигналы.
Этот цикл удобно записать на императивном языке, но на самом деле пофиг, так как он маленький.
При написании C++-шаблонов очень часто используется ФЯ. Взять тот же STL, там активно используется ФЯ-подход.
Опыт показывает, что программисты среднего уровня очень тяжело находят ошибки, даже если ФЯ использовался только в библиотеках, а им надо было только эти библиотеки поиспользовать.
Если же им надо самим что-то изобразить в этом стиле, то там, вообще, полные кранты.
Сначала мы описываем функции - фактически правила преобразования причем компьютер(компилятор) выбирает наиболее подходящий вариант, а также выбирает порядок выполнения
Ты путаешь что-то. В ФЯ компилятор ничего не выбирает, все детерминировано. Ты заранее знаешь, что и в каком порядке будет выполняться.
При перемене функций поведение программы действительно может измениться, но это экзотика и происходит это только потому, что в ФЯ можно использовать имена для функций и переменных сколько угодно раз и при изменении положения функции в файле некоторые нужные ей функции и переменные могли поменять значение. Но это очень маловероятная ошибка.
вот про это я не понял
маза тебя кто-то обманул
соответственно, разбрасывать функции по всей программе тоже незачем,
для этого придумали например модули
порядок вычислений выбирается автоматически только в ленивых языках, опять же *ML к ним не относятся
результат чистых функций не зависит от порядка вычислений по определению, для императивных участков
есть специальные конструкции, позволяющие управлять порядком появления побочных эффектов (это уже не ко мне, я не умею)
Управление или пониманием того, что надо сделать дальше?
Фактически - же задача состоит из двух задач: анализ ситуации, и выполнение на основе этого анализа каких-то действий.
Если анализ - это, конечно, в большей степени - ФЯ, то выполение - это уже скорее ИЯ.
Эта магия - монады.
Она позволяет строить композицию state transformer'ов,
которую исполнитель (интерпретатор/кусок скомпилированного кода)
может применять к соответствующему объекту (например, к файлу).
Допиши: "написаннный плохим программистом."
Это такая же сказка, как и нечитаемый код на Форте.
Я сравнивал гнутый код, на сях, и код на Форте.
Последний куда читаемее.
На ФЯ он получился бы не менее читаемым, чем на Форте.
Не рассказывай сказки.
---
...Я работаю антинаучным аферистом...
согласен
но это уже уровень драйвера устройства
за исключением маленького event loop, который пофиг на чём написан
Как быть, если нам необходимо передать какие-то данные из первой функции в последующую?
---
...Я работаю антинаучным аферистом...
В природе, идеальные программисты не встречаются. Идеальные программисты - пишут диссертации, на симпозиумы ездят, науку толкают, но реальные продукты не пишут.
Смысл от языка, если он применим только сферическим программистом в вакууме?
Рассмотрим сферического программиста в вакууме...
Так я уже передаю данные - kettle. Можешь считать, что это что угодно.
---
...Я работаю антинаучным аферистом...
скорее всего издержки бюрократии, тем более АДА-у они сами и разрабатывали.
Сам программист?
Скорее так:
"Для реализации абстракций из ФП в C++ приходится использовать шаблоны."
Получается очень хреново, но другого способа, по всей видимости, нет.
В результате "программистам среднего уровня" достаётся тяжело читаемый
и понимаемый код, причём представляющий неизвестную парадигму программирования.
Хотя если бы они были знакомы с ФП (хотя бы на основе "игрушечного" языка
то использовать эти шаблоны им бы стало намного проще (обращаю внимание,
что требуется только понимание основ ФП, а не реализация проекта на ФЯ).
---
...Я работаю антинаучным аферистом...
Не слышал об этом?
У любого языка есть стилистические правила, даже правила оформления.
Можешь посмотреть по сети.
---
Real FORTRAN Programmer can program FORTRAN in _any_ language.
Можешь пойти и почитать.
Сравни с сями.
Это не просто так, могу тебе сказать.
"Издержки бюрократии" --- это сказки.
---
...Я работаю антинаучным аферистом...
> В природе, идеальные программисты не встречаются.
> Идеальные программисты - пишут диссертации, на симпозиумы ездят,
> науку толкают, но реальные продукты не пишут.
>
> Смысл от языка, если он применим только сферическим программистом в вакууме?
По твоему программирование - это только то, что делают плохо обученные обезьянки?
PS А так любимый этими обезьянками M$ по-прежнему вкладывает бабло в ФП.
EmacsДа? А я вижу C: http://savannah.gnu.org/cgi-bin/viewcvs/emacs/emacs/src/
EnlightmentЕсли имеется в виду http://www.enlightenment.org, то смотрим http://cvs.sourceforge.net/viewcvs.py/enlightenment. Видим C.
GIMPЛень ссылку искать. Он тоже написан на C/C++.
ReduceЧто это? Можно сразу привести ссылку на исходники на ФЯ.
AutoCADРазве про его исходники что-то известно?
Как бы некоторые считают грамотным подходом в сложных проектах использовать несколько языков.
Мол, говорят, для разных подзадач подходят разные парадигмы.
То, что останется,--- это Лисп или Схема.
У Reduce код был всегда закрыт, насколько помню.
---
...Я работаю антинаучным аферистом...
Нет, я хочу сказать другое: язык программирование должен облегчать написание программ, а не усложнять.
Язык (подход) должен максимально легко стыковаться с MainStream-ом, чтобы можно было не изобретать велосипед.
На данный момент, я такой стыковки не вижу.
> PS А так любимый этими обезьянками M$ по-прежнему вкладывает бабло в ФП.
MS много куда сейчас вкладывает деньги, у MS довольно серьезный отдел Research-а, но при этом далеко не все разработки research-а реально используются.
---
...Я работаю антинаучным аферистом...
Я еще могу согласится, что в 1961 году Lisp был MainStream-ом, т.к. многие надеялись, что Lisp поможет быстро и просто разработать экспертные базы.
Почему ты считаешь, что Lisp до сих пор является Main stream-ом?
Тогда наверное Lisp туда не относится, пожалуй.
Хотя отдельные Success Stories и в этой области есть: http://www.paulgraham.com/avg.html
Не понимаю слова "стыковаться".
Например, для системной программы самое важное от языка - насколько легко делать системные вызовы
и другие низкоуровневые операции. Вот и вся стыковка.
Если с этим всё нормально, то уже довольно легко оценить, подходит ли язык для задачи.
Допустим я пишу программу. В любой большой программе мне скорее всего захочется:
посохранять данные между перезапусками,
поработать со стандартными форматами данных,
пообщаться с пользователем (вывести какие-то данные пользователю, ввести данные от пользователя
пообщаться с другими готовыми программами.
Может захотеться, что-то более специфичное:
серверное решение (работа с несколькими пользователями, разделение прав доступа, тонкий клиент и т.д. работа в качестве компонента для более большой системы,
возможность частичной автоматизии программы пользователем (поддержка скриптов
возможность расширения программы пользователем (плагины).
MainStream-овые средства разработки гарантирует, что все эти задачи уже худобедно решенны, и может даже доступные какие-то готовые компоненты, или хотя бы просто советы, как лучше решать все вышеперечисленные задачи.
Насколько все вышеперечисленные задачи решены в рамках того же лиспа?
За С/C++, VB, Java, Delphi, C# я могу четко сказать, что да - все вышеперечисленные задачи уже решены.
Lisp is worth learning for the profound enlightenment experience you will have when you finally get it; that experience will make you a better programmer for the rest of your days, even if you never actually use Lisp itself a lot.Как раз ответ на вопрос зачем изучать, если нет возможности применить в явном виде.
имхо, на данный момент стоит говорит только о прикладных задачах, т.к. с каждым днем с развитием железа все больше системных задач переходят в разряд прикладных.
Возмем банальное общение программы с какой-то внешней железкой: если раньше безусловно было необходимо писать системную программу, которая, напрямую, работала с портами и должна была укладывать в жесткие временные рамки.
Сейчас же все проще: системный уровень уже обычно давно до нас написан, а именно написан транспортный (канальный) уровень. На нашу же долю остается банальное формирование посылаемых данных, и разбор принятых данных, которые делается непосредственно на прикладном уровне.
> Не понимаю слова "стыковаться".
Это значит уметь подсоединять библиотеки, модули, готовые программы, идеи, которые разработаны в рамках mainstream-а
Маза не в любой. Похоже, ты только о своей конкретной области думаешь.
И область эта - бухгалтерия (у тебя все примеры оттуда).
И правильнее говорить не о программе, а о какой-то системе или решении.
Отличия:
- Сразу же становится ясно, что разные компоненты могут быть на разных языках
- Часть компонентов может быть уже сделаны.
Теперь моё отношение к этим вещам:
Я ни разу не профессиональный разработчик, и не собираюсь им становиться,
программирую, когда есть конкретная задача.
Если можно решить без программирования - тем лучше.
Предположим, нельзя.
Тогда нужно решить, как проще сделать то, что нужно.
Какой-нибудь опытный разработчик и с неудобным языком управится,
а я без удобного инструмента не обойдусь, не получится просто.
И получившееся скорее всего не будет продуктом - нафиг надо?
нежели ядро ОС и драйвера устройств.
И вообще, какое-то у тебя странное представление об этих делах,
думаешь, сейчас драйверов, напрямую работающих с портами, уже не делают?
Вот пример, уже более близкий к бухгалтерии, роутер с учётом трафика.
В целом, система сложная, а собственно программировать надо немного.
Вот и использование готовых библиотек, идей, компонентов!
Но среди того, что нужно дописывать, есть и системные части, и прикладные.
Если же говорить за mainStream-овские задачи, то на данный момент mainstream-ом является не бухгалтерия, а более широкий класс задач: автоматизация (вернее сопровождение) повседневных задач, стоящих перед людьми.
Возьмем произвольного человека и понаблюдаем немного за ним:
1. Человек проснулся по будильнику (О! будильник. это можно автоматизировать: автоматизировать поведение будильника от ситуации: будни, выходные, праздник, бюлетень, равно встать - встретить поезд, у членов семьи разное расписание - один встает в 9, другой в 10 и т.д.)
2. Сделал зарядку (это тоже можно автоматизировать: формирование программы упражнений, отслеживание статистики самочувствия и т.д.)
3. Едит на работу (подсказка эффективного маршрута, оповещения о пробках, ремонтах, гаишниках и т.д.)
4. Формирование дел на сегодня (это - банально)
5. Общение с заказчиками, клиентами, покупателями (ведение картотеки по данным людям, ведение картотеки по проектам)
6. Написал пару бумажек начальнику/подчиненным (документооборот)
7. Получил зарплату (наша любимая бухгалтерея)
8. Поехал в магазин за покупками (картотека продуктов, оптимизация выбора и т.д.)
9. Приготовил ужин (картотека блюд, помощь в приготовлении блюд, помощь в покупке продуктов)
10. Досуг (картотека культурных развлечений, фильмов, книг и т.д.)
Все эти задачи построены одиннаковым образом, но при этом каждая имеет свои особенности. При чем этих задач не то, что много, а очень много. По этому на обширном фоне этих задач, все остальные задачи: системые, вычислительные и т.д., выглядят, как капля в море.
Возьмем все имеющиеся ресурсы в сети:
1. lorien.local - картотека (база знаний) о файлах в сети
2. z80 - картотека (база знаний) о фильмах в сети
3. forum - картотека (база знаний) нашего общения, наших мыслей
4. vnk.local - картотека (база знаний) о сотовой связи
5. Биллинги - картотека (база знаний) о сеансах связи с инетом
и т.д.
Все эти задачи объединяет следующие: есть некие данные, эти данные хранятся, структурируются (для этого, например, на форуме модераторов даже заводят живут своей жизнью (меняются, устаревают, преобразуются по этим данным собирается статистика, формируется советы по оптимальному использованию (например, слово (new) рядом с тредом подсказывает о том, что в данный тред стоит зайти учитываются требования пользователя (банальное изменение внешнего вида) и т.д.
Во всех этих задачах, я не вижу применимость ФЯ в качестве основного средства программирования.
Да, его можно использовать по чуть-чуть, то здесь, то там, но не более.
> ФЯ в качестве основного средства программирования.
Просто инертность мышления.
>Возьмем все имеющиеся ресурсы в сети:
> и т.д.
Новый switchmap написан на ocaml.
В нём мне удобнее работать с деревом.
зачем это взаимодействие делать на системном уровне, если проще сделать на прикладном? Открыл tcp/ip соединие и вперед общайся по самое не хочу.
Стыковка с остальными частями программы идет через файлы, или напрямую?
А если я вижу, в некоторых?
А применимость функционального стиля в качестве основного, при использовании
мультипарадигмальных языков - так совсем хорошо вижу.
Что такое "остальные части программы"?
Например?
> А применимость функционального стиля в качестве основного, при использовании
мультипарадигмальных языков - так совсем хорошо вижу.
В качестве основного не получится, т.к. event loop все равно внешний
> Открыл tcp/ip соединие и вперед общайся по самое не хочу.
С PCI-картой ты будешь по TCP/IP общаться? LOL
Форум, файлоискалка, биллинг - то, про что я немного знаю.
> В качестве основного не получится, т.к. event loop все равно внешний.
Я под словом "основное средство" понимаю то, что используется для большей
части содержательного кода, не относящегося к категории готовых библиотек и компонентов.
Например, в случае форума сейчас это именно скрипты на PHP,
а вовсе не ядро Linux и не apache (хотя event-loop-ы все там)
Встроенные решения уже намного реже делают.
зы
вот, например, в ряде промышленных компьютеров, в качестве шины используют gigabit ethernet, в которую втыкаются отдельные части компьютера
> используют gigabit ethernet, в которую втыкаются отдельные части компьютера
это всё хорошо и прогрессивно в некоторых случаях
я на самом деле очень рад таким тенденциям
ссылку можешь дать?
но для гигабитных карт нужен драйвер
и для гигабитного свитча нужно firmware
и рост скорости железа ничего не решает, так как так же
растут и требования к скорости передачи
и модификации TCP/IP (как протокола, так и реализации)
нужны при росте скоростей
части содержательного кода, не относящегося к категории готовых библиотек и компонентов.
Тогда уж больше будет декларативного кода. Кода, который описывает данные, их взаимосвязи, ограничения и т.д.
Тот же sort, поиск и т.д. пишется только один раз.
Если немного помечтать о будущем, то в идеале будет следующее:
1. Декларативное описание данных
2. Логические правила преобразования данных
3. Немного ФЯ в качесте подсказки компьютеру для тех случаев, где он не смог сам справиться.
но, имхо, ФЯ в том виде, в котором оно сейчас почти непригодно в вышеперечисленных задачах.
Например, пользовательский интерфейс.
не вижу в этом проблем для использования ФП
> но, имхо, ФЯ в том виде, в котором оно сейчас почти непригодно в вышеперечисленных задачах.
Как мы выяснили, у тебя весьма странные представления о функциональном программировании,
и о языках.
Может быть, пока не время столь категорично выражаться?
Это самое простое решение и я пока не вижу причин здесь что-то менять.
Остальные части пользовательского интерфейса, например, статус свитча
и подключенных к нему компов или поиск пути между компами,
сделаны на ocaml целиком.
и о языках.
> Может быть, пока не время столь категорично выражаться?
У меня о ФЯ до этого треда было чуть идеализированное представление, мне казалось, что в ФЯ намного больше используется декларативный подхода, чем в ИЯ.
На практике оказалось, что в ФЯ используется только одно единственное логическое правило - это то, что рекурсию можно перевести в цикл.
но, имхо, как раз это отличие не очень важно.
Намного интереснее следующие вещи:
например:
sort(items) преобразуется в qsort(items)
sort({i}) преобразуется в {i}
sort({i, j}) преобразуется в if (i < j) return {i, j}; else return {j, i};
sort(items, desc)[0] преобразуется в max(items)
и т.д.
Декларативный подход (в частности, логическое программирование) как раз может все это обеспечить.
А ФЯ, насколько я понял, все это обеспечить не может. увы...
---
...Я работаю антинаучным аферистом...
Событийное программирование.
Появилось в Лиспе.
> формирование программы упражнений, отслеживание самочувствия и т.д.)
Экспертная (маленькая такая) система?
Появилось в Лиспе.
> 3. Едит на работу (подсказка эффективного маршрута,
> оповещения о пробках, ремонтах, гаишниках и т.д.)
Поиск в сетях, расписание маршрутов.
Тоже Лисп.
> 4. Формирование дел на сегодня (это - банально)
E-Lisp.
> 5. Общение с заказчиками, клиентами, покупателями
> (ведение картотеки по данным людям, ведение картотеки по проектам)
Легко.
> 6. Написал пару бумажек начальнику/подчиненным (документооборот)
Emacs
> 7. Получил зарплату (наша любимая бухгалтерея)
Это что подразумевается?
> 8. Поехал в магазин за покупками (картотека продуктов,
> оптимизация выбора и т.д.)
Планирование тоже появилось в Лиспе.
> 9. Приготовил ужин (картотека блюд, помощь в приготовлении блюд,
> помощь в покупке продуктов)
То же.
> 10. Досуг (картотека культурных развлечений, фильмов, книг и т.д.)
Базу данных сделать не вопрос.
> 5. Биллинги - картотека (база знаний) о сеансах связи с инетом
и т.д.
К Полу Грахаму.
> Во всех этих задачах, я не вижу применимость ФЯ
> в качестве основного средства программирования.
Тем не менее все эти задачи исконно решаются на Лиспе.
Учи матчасть.
---
...Я работаю антинаучным аферистом...
> sort({i}) преобразуется в {i}
> sort({i, j}) преобразуется в if (i < j) return {i, j}; else return {j, i};
В чём проблема-то?
Берёшь и декларируешь.
sort :: Ord a => [a] -> [a]
sort (i:[]) = [i]
sort (i:j:[]) = if i<j then [i,j] else [j,i]
sort a = qsort a
только по-аглицки или со скобками.
В некоторых ФЯ, кстати, есть ленивые вычисления сразу.
В других ФЯ их можно сделать.
---
...Я работаю антинаучным аферистом...
А трансляторы из ФЯ в ИЯ есть? Например, для Java-ы?
> sort (i:[]) =
сложные условия декларировать можно?
например, что-то такое:
sort (items where items.Length > 1000000) сортироватьСлиянием(items)
Вроде, видел упоминание и о "Яве."
"Яву," вообще-то не стоит рассматривать --- ВМ нафиг ненужна.
---
...Я работаю антинаучным аферистом...
Это не условия, как ты думаешь, а сопоставление с образцом.
---
...Я работаю антинаучным аферистом...
У связки фп+ява могут быть свои достаточно интересные применения.
Например, можно попытаться сгенерить эффективный генератор
компилятора произвольного языка в яву по интерпретатору этого языка,
написанному на яве, то есть, фактически, по спецификации.
Для этого нужно написать на яве самоприменимый специализатор явы
(что весьма сложно) или написать его на ФЯ и скомпилить в яву
(это тоже сложно, но всё же значительно проще а потом
дважды применить его к себе (3я проекция Футамуры-Турчина).
С лиспом и рефалом какие-то успехи в этой области уже были,
явой несколько лет назад занимались, а вот с C такое сделать не получится.
Кстати, MS вроде чем-то таким тоже интересовалась,
а может уже и использует во всю.
Можно по-русски?
"Ява" на "Яве," что ли?
Да и причём здесь "Ява?"
Любая ВМ подойдёт, как я понимаю.
---
...Я работаю антинаучным аферистом...
> Можно по-русски?
> "Ява" на "Яве," что ли?
На входе программа на J и некоторые её входные данные,
на выходе - программа на J, уже использующая эти данные.
Причём, реализовано всё тоже на J, и эту реализацию
можно подсунуть на вход программы.
> Да и причём здесь "Ява?"
> Любая ВМ подойдёт, как я понимаю.
Любая конечно.
А ты говоришь "нафиг не нужна".
Я правильно понимаю?
У "Явы" очень плохая ВМ.
Медленная.
---
...Я работаю антинаучным аферистом...
> Я правильно понимаю.
Да, только это частичное применение может быть гораздо сложнее
подстановки констант в нужные места программы, и может включать
достаточно сильную трансформацию кода или вообще генерацию его заново.
На относительно игрушечном языке (пример из дисера) мне однажды показывали,
как из функции поиска подстроки в строке методом последовательного прикладывания образца
в результате специализации по подстроке, которую ищем, получался
алгоритм КМП для её поиска.
Ну, это понятно.
> алгоритм КМП для её поиска.
Сокращение не понял.
---
"...Надо учиться --- не напрягаясь!.." Акад. А. А. Бучаченко.
Алгоритм Кнута-Морриса-Пратта.
То же самое на C записывается с помощью конструкции if.
Теперь ты наверное логическое программирование идеализируешь,
а там тоже нет чудес.
Все "чудесные" результаты, вроде суперкомпиляции или как там
называется то, про что рассказывает, придумываются
теоретиками, и мейнстрим узнает про это в последнюю очередь.
По определению, ведь до этого нет готовых библиотек, модулей, советов.
Ну бывают, конечно, только ИЯ в качестве ассемблера используются обычно -
более высокоуровневые конструкции едва ли подойдут из-за другой системы типов хотя бы.
Как тут правильно заметили, изучение разных парадигм программирования полезно тем, что дает возможность иначе взглянуть на решаемые проблемы, даже если ты никогда не напишешь ни одной программы на языке отличном от C#, например. Здесь такая же ситуация, как и с естественными языками, жалкими подобиями которых являются компьютерные. Выучив другой язык ты получаешь возможность взлянуть на мир через призму другой культуры, осмыслить его в совершенно иных и необычных терминах. Твоя проблема сейчас в том, что ты не знаешь, что значит мыслить функционально, ты пытаешься сравнивать ФЯ и ИЯ с позиций ИЯ, так как ты привык решать задачи на них и требуешь от нас, чтобы мы тебе показали преимущества ФЯ на каких-то искуственных примерах. Это бессмысленный подход, как я мог бы, например, доказать человеку, который не знает английского, что он ничем не хуже русского, а просто другой? Есть только один путь, чтобы понять разницу - выучить самому один из ФЯ, благо есть книги и по ML и по OCaml и по Haskell. Я думаю, ты не пожалеешь о потраченном времени, тем более, что ФЯ синтаксически гораздо проще императивных и учатся гораздо быстрее. Взамен ты узнаешь, что есть совсем иной взгляд на то, как должны выглядеть программы, настолько другой, что в свое время для меня было сильным потрясением узнать о нем.
Опять ты испортил мое идеализированное представление о ФЯ
> Теперь ты наверное логическое программирование идеализируешь,
а там тоже нет чудес.
Логическое программирование пока отложим.
Меня на данный момент интересуют в первую очередь декларативные языки.
Мне казалось, что ФЯ как раз являются декларативными языками, но оказалось, что в ФЯ этой самой декларативщины лишь малая часть.
> Все "чудесные" результаты, вроде суперкомпиляции или как там
называется то, про что рассказывает, придумываются
теоретиками, и мейнстрим узнает про это в последнюю очередь.
По определению, ведь до этого нет готовых библиотек, модулей, советов.
Это понятно. И с этим я согласен.
Но одно дело, когда эти "чудесные" результаты - совершенно оторваны от MainStream-а и непонятно, что с ними тогда делать в реальной жизни.
А совсем другое дело, когда эти "чудесные" результаты после доработки напильником могут быть применены в реальных проектах (для этого и должна быть стыковка с mainStream-ом).
ФЯ я знаю (по крайней мере в рамках Лиспа и могу писать программы в функциональном стиле (вышеперечисленные приведенные примеры на ФЯ, я поматерившись на синтаксис, смогу написать)
но при этом я не вижу принципиальной разницы между ИЯ и ФЯ, и там, и там мы пишем, как надо делать, а не что надо делать. Какая разница как записывать работу над списком? В виде рекурсии, или в виде цикла? Рекурсивный вариант только чуть более близок к математической записи, но не более.
Но я вижу, что вокруг ходит народ и восторгается этим самым функциональным подходом.
В результате этого противоречия (вижу/не вижу) возникает мысль, что может я что-то недопонял в ФЯ? Может нынешние функциональные языки (ML, OCaml, Haskell и т.д.) умеют больше, чем я о них думаю на примере Лиспа?
На основе данного треда, все-таки вытанцевывается мысль, что в ФЯ сильно прогрессивных идей, по сравнению с тем же C++, не несут.
И рекурсия и цикл могут быть спрятаны.
Смотри хотя бы те же map и filter.
Ленивые вычисления даже на Лиспе делать куда проще, чем на ИЯ.
В отличие от Лиспа ни у одного ИЯ нет единства программ и данных.
---
"Надо учиться --- не напрягаясь!"
У ФЯ тоже
МЛ-ы мне не так понравились.
---
"А я обучался азбуке с вывесок,
листая страницы железа и жести."
> Смотри хотя бы те же map и filter.
По этому я и говорю, что разницы между ИЯ и ФЯ нет, потому что мы можем реализовать map и filter и там, и там, а далее будет код один в один.
Что понимается под "единством программы и данных"?
Лисп - обычно не считается ФЯ, на нём можно писать в любом практически стиле.
> но при этом я не вижу принципиальной разницы между ИЯ и ФЯ, и там, и там мы пишем, как надо делать, а не что надо делать.
Я понимаю так, что это общее свойство языков программирования.
В отличии от других языков.
О декларативных языках.
Тут имхо надо разделять ситуации: одно дело, когда необходимо понимание, что происходит в интерпретаторе,
и тогда это язык программирования: фактически, человек пишет так, чтобы "за кулисами" происходило
то, что ему нужно.
И другое дело, если это понимание не требуется, тогда это не программирование, а использование программы.
ФП и ИП - это разные представления алгоритма.
Иногда удобнее одно, иногда другое.
> Но я вижу, что вокруг ходит народ и восторгается этим самым функциональным подходом.
Народу нравится функциональное представление
> В результате этого противоречия (вижу/не вижу) возникает мысль, что может я что-то недопонял в ФЯ?
Возможно, ты не видишь чистых функций, и соответственно потенциальной области применения ФП,
там, где этот народ их видит?
Такое предположение согласуется с твоими словами, что ты почему-то считаешь ФП неприменимым
там, где другие видят применения.
> На основе данного треда, все-таки вытанцевывается мысль, что в ФЯ сильно
> прогрессивных идей, по сравнению с тем же C++, не несут.
В С++ неудобно писать в функциональном стиле, а идея это или нет, уж не знаю.
Просто отсутствуют некоторые конструкции языка, и чтобы получить возможности ФЯ,
фактически нужно писать интерпретатор или самому поработать компилятором.
А Лисп - случай особый, вследствии единообразия кода и данных.
В частности, насколько я понял (точно скажу, когда книжку дочитаю
магию вроде этого yield можно реализовать штатными средствами.
И любую другую магию такого плана.
Например, ленивость, мемоизацию, или там заставить
любую программу чек-поинты периодически делать.
Но тут я не уверен - книжку не дочитал.
Вперёд.
map из R^5RS на "C."
> Что понимается под "единством программы и данных"?
Оно и понимается.
См. lambda.
---
...Я работаю антинаучным аферистом...
Эту фразу не понял.
> Возможно, ты не видишь чистых функций, и соответственно потенциальной области применения ФП,
там, где этот народ их видит?
Можно ли сказать, что в sql-е много чистых функций?
А ведь как раз большую часть кода в прикладных программ можно описать в рамках sql-я.
На МЛ (в т. ч. и Хаскелле) тоже можно.
Вывод.
> О декларативных языках.
> Тут, имхо, надо разделять ситуации:
> одно дело, когда необходимо понимание, что происходит в интерпретаторе,
> и тогда это язык программирования: фактически, человек пишет так,
> чтобы "за кулисами" происходило то, что ему нужно.
Верное замечание.
Порядок вычисления аргументов произволен, в общем случае.
> Возможно, ты не видишь чистых функций,
> и соответственно потенциальной области применения ФП,
> там, где этот народ их видит?
Чистые функции тоже имеют своё назначение.
Именно в смысле порядка вычислений и параллельности.
А ещё и для доказательства корректности.
> В С++ неудобно
Невозможно.
Функции неравноправны с обычными данными.
> А Лисп - случай особый, вследствии единообразия кода и данных.
> В частности, насколько я понял (точно скажу, когда книжку дочитаю
> магию вроде этого yield можно реализовать штатными средствами.
См. Лисп на Лиспе по Самому Маккарти.
Придётся сделать переключающий цикл.
> Например, ленивость,
Легко.
> мемоизацию,
Не понял, что ты имеешь в виду.
Замыкание, что ли? "function"?
> или там заставить любую программу чек-поинты периодически делать.
Тоже не понял.
---
"А я обучался азбуке с вывесок,
листая страницы железа и жести."
> В частности, насколько я понял (точно скажу, когда книжку дочитаю
магию вроде этого yield можно реализовать штатными средствами.
> И любую другую магию такого плана.
Например, ленивость, мемоизацию, или там заставить
любую программу чек-поинты периодически делать.
На SmallTalk-е эта магия тоже активно делается и используется.
Сообщи, какого типа значение вовзращает твой map.
---
...Я работаю антинаучным аферистом...
> Эту фразу не понял.
Возьмём например декларативный язык SQL.
Если ситуация такая, что можно написать любой запрос из эквивалентных,
не заботясь об например об эффективности, то это не программирование.
Всё уже запрограммировали авторы СУБД.
Если же нужно думать, какие создать индексы, и как потом сформировать запрос,
не дай бог с хинтами оптимизатору, вот это уже можно рассматривать
как программирования, но и декларативности поубавилось.
В чём разница?
В обоих случаях человек формулирует, какой нужно результат получить.
Но во втором случае оказывается не всё равно, как он будет получен,
и тут начинается программирование.
> А ведь как раз большую часть кода в прикладных программ можно описать в рамках sql-я.
Имхо неправда.
Самая интересная часть - это та, которая определяет, когда какие запросы
выдавать, в каком порядке, с какими параметрами, и как использовать результаты.
И на SQL это не выражается (если процедурные расширения туда не относить).
Вот там уже дофига можно найти чистых функций.
А SQL - это всего лишь элемент API.
> На МЛ (в т. ч. и Хаскелле) тоже можно.
> Вывод.
На Лиспе - примерно одинаково удобно в любом стиле.
На ФЯ - в языке специальные конструкции для функционального стиля.
> Порядок вычисления аргументов произволен, в общем случае.
Я вообще то далеко не только про порядок, а вообще про все действия.
Декларативный язык - специфицирует только желаемый результат.
Язык программирования - описывает способ получения результата.
> > В С++ неудобно
> Невозможно.
Возможно скомпилировать функциональную программу в C++.
В том числе и вручную, примеры приводил в одном из прежних флеймов.
> > мемоизацию
запоминание результата чистой функции, чтобы потом,
если её вызовут с тем же аргументами, просто подставить
тот же результат
> > заставить любую программу чек-поинты периодически делать
ну типа есть программа
а мы из неё получаем (прозрачно для пользователя) другую,
которая периодически сохраняет состояние, для обеспечения
отказоустойчивости
мы это обсуждали как-то уже
Ещё вспомнил:
исполнение недоверенных апплетов.
Можно получить выражение (программу) и например расставить необходимые проверки
доступа в соответствии с политикой безопасности, причём по ходу доказать как можно
больше полезных свойств, которые позволят сэкономить на проверках.
Да!
Это тоже язык с тем же свойством, как я понял из тонкой книжки.
> Сообщи, какого типа значение вовзращает твой map.
Открываем файл <algorithm> из поставки любого компилятора C++:
[c]
// TEMPLATE FUNCTION transform WITH UNARY OP
template<class _InIt,
class _OutIt,
class _Fn1> inline
_OutIt transform(_InIt _First, _InIt _Last, _OutIt _Dest, _Fn1 _Func)
{ // transform [_First, _Last) with _Func
for (; _First != _Last; ++_First, ++_Dest)
*_Dest = _Func(*_First);
return (_Dest);
}
// TEMPLATE FUNCTION transform WITH BINARY OP
template<class _InIt1,
class _InIt2,
class _OutIt,
class _Fn2> inline
_OutIt transform(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2,
_OutIt _Dest, _Fn2 _Func)
{ // transform [_First1, _Last1) and [_First2, _Last2) with _Func
for (; _First1 != _Last1; ++_First1, ++_First2, ++_Dest)
*_Dest = _Func(*_First1, *_First2);
return (_Dest);
}
[/c]
пример использования (из документации):
[c]
Example
// alg_transform.cpp
// compile with: /EHsc
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
// The function object multiplies an element by a Factor
template <class Type>
class MultValue
{
private:
Type Factor; // The value to multiply by
public:
// Constructor initializes the value to multiply by
MultValue ( const Type& _Val ) : Factor ( _Val ) {
}
// The function call for the element to be multiplied
int operator ( ) ( Type& elem ) const
{
return elem * Factor;
}
};
int main( )
{
using namespace std;
vector <int> v1, v2 ( 7 v3 ( 7 );
vector <int>::iterator Iter1, Iter2 , Iter3;
// Constructing vector v1
int i;
for ( i = -4 ; i <= 2 ; i++ )
{
v1.push_back( i );
}
cout << "Original vector v1 = ( " ;
for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; Iter1++ )
cout << *Iter1 << " ";
cout << ")." << endl;
// Modifying the vector v1 in place
transform (v1.begin ( ) , v1.end ( ) , v1.begin ( ) , MultValue<int> ( 2 ) );
cout << "The elements of the vector v1 multiplied by 2 in place gives:"
<< "\n v1mod = ( " ;
for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; Iter1++ )
cout << *Iter1 << " ";
cout << ")." << endl;
// Using transform to multiply each element by a factor of 5
transform ( v1.begin ( ) , v1.end ( ) , v2.begin ( ) , MultValue<int> ( 5 ) );
cout << "Multiplying the elements of the vector v1mod\n "
<< "by the factor 5 & copying to v2 gives:\n v2 = ( " ;
for ( Iter2 = v2.begin( ) ; Iter2 != v2.end( ) ; Iter2++ )
cout << *Iter2 << " ";
cout << ")." << endl;
// The second version of transform used to multiply the
// elements of the vectors v1mod & v2 pairwise
transform ( v1.begin ( ) , v1.end ( ) , v2.begin ( ) , v3.begin ( ) ,
multiplies <int> ( ) );
cout << "Multiplying elements of the vectors v1mod and v2 pairwise "
<< "gives:\n v3 = ( " ;
for ( Iter3 = v3.begin( ) ; Iter3 != v3.end( ) ; Iter3++ )
cout << *Iter3 << " ";
cout << ")." << endl;
}
Output
Original vector v1 = ( -4 -3 -2 -1 0 1 2 ).
The elements of the vector v1 multiplied by 2 in place gives:
v1mod = ( -8 -6 -4 -2 0 2 4 ).
Multiplying the elements of the vector v1mod
by the factor 5 & copying to v2 gives:
v2 = ( -40 -30 -20 -10 0 10 20 ).
Multiplying elements of the vectors v1mod and v2 pairwise gives:
v3 = ( 320 180 80 20 0 20 80 ).
[/c]
Это свойство называется "closure"?
позволяет создавать самомодифицирующийся код.
Характерно ещё для маш.кода
Пользоваться тем легче, чем проще сгенерировать код и запустить его.
В Лиспе - проще всего, наверное.
Сравни объём кода:
(define (orl . l)
(if (null? l) #f
(if (car l) #t (apply orl (cdr l
(define (mapcar f l)
(if (null? l) l
(cons (f (car l (mapcar f (cdr l
(define (map f . l)
(if (apply orl (mapcar null? l '
(cons (apply f (mapcar car l
(map f (mapcar cdr l
Это определение.
Просто тупо напрямую.
f в map --- функция какой угодно арности.
Одно только использование в Си уже неудобно.
Сможешь написать так?
(map + '(1 2 3 4) '(5 6 7 8
Или тебе придётся обёртку для плюса делать?
---
...Я работаю антинаучным аферистом...
Сейчас сложно сказать. Так сразу я не вспомнить использование многоарных функций.
> Или тебе придётся обёртку для плюса делать?
#include <algorithm>
#include <functional>
#include <iterator>
#include <vector>
int _tmain(int argc, _TCHAR* argv[])
{
int f1[] = {1, 4, 2};
int f2[] = {3, -4, 1};
std::vector<int> result;
std::transform(&f1[0], &f1[sizeof(f1)/sizeof(int)], &f2[0],
std::back_insert_iterator<std::vector<int> >(result
std::plus<int>
return 0;
}
Построение кубического сплайна, например.
Выкинь две строки с присвоением значений массивов и посмотри,
насколько много тебе приходится писать лишнего.
Начиная с "#include" и заканчивая постоянным "std::" и непонятным "sizeof(int)."
Почему сделано взятие первого члена "[0]"?
Почему есть смешение понятий массива и неизвестно чего, обозначенного "std::vector?"
Почему введены переменные f1, f2?
У меня не было никаких вспомогательных имён.
Кстати, почему ты не можешь "#include" написать в одну строчку?
Наподобие такого:
(require "this" "this" "and this")
Это настолько просто сделать, что не сделать этого --- преступление.
То, что ты привёл, --- это не функциональный стиль.
Должно быть так:
(max (map + '(1 2 3 4) '(5 6 7 8
А у тебя --- фортраноподобное "call solve(in1, in2, rslt)."
---
...Я работаю антинаучным аферистом...
> Почему есть смешение понятий массива и неизвестно чего, обозначенного "std::vector?"
> Почему введены переменные f1, f2?
Потому что C++ - уродский язык с уродскими legacy-конструкциями для сохранения максимальной совместимости с C
> То, что ты привёл, --- это не функциональный стиль.
> Должно быть так:
> (max (map + '(1 2 3 4) '(5 6 7 8
> А у тебя --- фортраноподобное "call solve(in1, in2, rslt)."
В чем разница?
Ты пишешь примерно так: call map(f,x,result)
А должно быть так: result=map(f,x)
---
...Я работаю антинаучным аферистом...
Половина, если не больше, "причин" весьма и весьма спорны.
Некоторые вообще неверны.
Ты смотрел внутрь Glibc?
---
...Я работаю антинаучным аферистом...
> А должно быть так: result=map(f,x)
во-первых, я пишу вот так:
call map(f, x, шняга_в_которую_надо_положить_result)
во-вторых, это уже придирки к синтаксису
> В отличие от Лиспа ни у одного ИЯ нет единства программ и данных.А в Python вообще все, что можно является объектами, включая переменные, функции и модули.
У ФЯ тоже
Чем не единство?
Нет не смотрел, а стоит?
Как ты напишешь такое?
(foldr + (map * list1 list2
---
...Я работаю антинаучным аферистом...
Увидишь, чего стоят все эти ИЯ с никакой расширяемостью.
Размышлять над словом "метапрограммирование."
---
...Я работаю антинаучным аферистом...
Функции ты умеешь создавать?
Сравни: (map (list 'lambda '(x) (read 1 2 3 4)
---
...Я работаю антинаучным аферистом...
Да, функции можно создавать на лету. На самом деле, в питоне (по крайней мере в версиях 2.x) имеются lambda, map, filter и reduce. Но я его функциональными возможностями пока не пользовался на практике.
По поводу статьи. Все возражения там или сопровождаются объяснением, почему на самом деле это возражение несущественно, или связаны с недостаточным финансированием (отладчики, библиотеки и т.п.) . На самом деле статья не о том, почему не нужно использовать ФЯ, а про то, почему это делать надо, но есть пока некоторое проблемы, препятствующие этому.
Зачем так категорично? Вот смотрю, например, как инженеры-проектировщики лихо пишут в АвтоКАДе на Lisp'е свои какие-то задачи.Эти люди не программисты, к тому же большинство из них кроме АвтоКАДа компьютерами не пользуется.
ps
На мой взгляд, утверждение "для изучения ХХХ нужно иметь довольно продвинутые мозги" лишено смысла. В общем виде в абстрактной теории все может казаться сложным и задвинутым, но если есть конкретные задачи, которые однозначно делаются на ХХХ, любой здравомыслящий вменяемый человек способен освоить.
#include <algorithm>
#include <functional>
#include <iterator>
#include <vector>
using namespace std;
int main
{
int f1[] = {1, 4, 2}, f2[] = {3, -4, 1};
vector<int> result;
transform(f1, f1+sizeof(f1)/sizeof(int f2, back_inserter(result plus<int>
return 0;
}
симпатичнее смотрится
а еще у меня есть самопальная либа в которой это все можно было бы сделать так:
#include "vectop.h"
using namespace cmpml;
void main
{
array1d<int> a, b, c;
a=1, 4, 2; b=3, -4, 1;
c=a, b;
}
интересно, Контру это устроит?..
В больших приложения за такое убивают. Потому, что очень много идет пересечений по идентификаторам (vector, string, min, max и т.д.)
> int f1[] = {1, 4, 2}, f2[] = {3, -4, 1};
Опять же не рекомендуется при разработке в команде, т.к. читабельность падает. запятую лучше, вообще, стараться не использовать, потому что она приводит к довольно трудновидимым глюкам.
например:
double pi = 3,1415;
Ошибку в этом коде можно искать очень долго.
> a=1, 4, 2; b=3, -4, 1;
в команде, я бы с запятой не баловался бы. тем более данной кусок изменяет семантику оператора запятая
Наша эпоха — это не только эпоха технологических метамарфоз. Наша эпоха — это эпоха систем миграции. Ведь это чудо, когда старый, уже никому не нужный код, но давно отлаженный и проверенный годами, сможет эволюционировать и породить таким образом новый, готовый к интеграции с самыми последними технологическими достижениями.
На смену двухуровневой технологии больших информационных систем пришла трёхуровневая, не буду вдаваться в природу и преимущества настоящего скачка, но отмечу и, может быть, повторюсь, что миллионы строк отлаженного кода, например, написанного на языке PL/SQL могут кануть в лету. Только грамотная система миграции может спасти золото наших отцов. По сути, система миграции — это компилятор, не в узком смысле, когда имеется в виду превращение текста на языке программирования в машинно-зависимый бинарный файл, но в истинном смысле, означающим преобразование текста одного языка в текст другого языка, будь то реальный язык программирования или машинные коды. Формальный язык или нет (в последнем случае — это уже задачи AI для реализации этого процесса нужно сосредоточиться именно на сути проблемы, но не на системных задачах подобных «проблемам» расположения кнопочек на форме, ползущей надписи, выделения памяти, ручной сборки мусора, в конце концов, сворачивания языка в трубочку и других ухищрения, что по сути вызывает глубинные мутации, в том числе и у воспринимающего субъекта — того програмиста, который вместо того, чтобы изливать свою душу на красивом языке постоянно ругается словами йоб твайу, пидарасы, гандоны, твердит о четвертовании била гейтса и ритуальном сожжении ОС Windows вместе со своими распрекрасными VC, COM, GDI, ADO, IUnknown, .NET, и другими высокотехнологичными словами, а потом с пола устало собирает остатки клавиатуры, искоса поглядывая в монитор на ящик сообщения, гласящий "Access violation", он, монитор, может быть, тоже не будет обделён...
Ява — это хороший раскрученный язык, но, товарищи, истинная сила в pattern matching, по крайней мере, в насущной задачи миграции. Я не буду объяснять, что это такое для тех, кто не знает, для этого есть очень много соответствующей литературы, но, товарищи драйверописатели, бухгалтеры и другие, в частности, все любители проектов «с нуля», не доказывайте со слюнями, что ваш язык круче. Дискуссия, как обычно, превратится в мерянье письками.
ML — вот пример языка, отлично подходящего для вышеназванной задачи. Как пример готового проекта, дам ссылку:
http://www.fdc.ru/portal/page?pageid=60,54746&_dad=portal&_schema=PORTAL
это адрес страницы, на которой описывается проект миграции из PL/SQL в современные Java технологии. Компилятор полностью написан на функциональном языке SML. Касательно процесса разработки — если программа откомпилировалась и у программиста нет ошибки в ДНК, то, скорее всего, все заработает правильно. Исключением могут быть лишь те случаи, когда программист не до конца понимает поставленной перед ним задачи. Касательно непопулярности языков ФП скажу лишь, что почти все знакомые мне программисты банально не понимают и не умеют пользоваться рекурсией. А заявления, подобные «что это за язык такой, без циклов, без настоящих переменных, без гуи, и не поддерживающий COM» — это те же самые заявления, что и о плоской Земле. Вскормленные на хелловордных книгах, люди не в состоянии вполне оценить прелесть функционального программирования. Это лишь наблюдение… Я ни в коем случае не пропагандирую ФП, я лишь отмечаю, что красота должна быть выразима и не должна иметь предела. Выбирайте сами язык, на котором вы лучше всего признаетесь в любви вычислительной технике, и, конечно же, себе самому как высококвалифицированному специалисту.
Сначала пара вопросов:
что такое тип в понятие ML-я?
можно ли на нем описать тип натуральное число? как это будет выглядить? Что это нам даст?
можно ли описать, например, тип C-идентификатор - это строка, с рядом ограничений
можно ли описать, например, тип идентификатор - это нечто, которое можно между собой сравнивать.
тип круг - это штука, у которой есть поля радиус и центр, и которая поддерживает операции изменитьПоложение, ВернутьОсиСимметрии и т.д.
тип невырожденный эллипс - это штука, с полями большой, малый радиус, центр, с операциями ВернутьФокусы, изменитьБольшойРадиус и т.д.
Чтобы понять язык, нужно научиться на нём думать, а не переводить с него на родной и обратно.
Вот ты английские термины часто используешь, почему не переводишь на русский?
Твои вопросы означают: а можно ли программу на C++ перевести в SML?
Да, конечно можно, но смысла не прибавится, наоборот, станет длиннее и менее понятно.
Как если бы слова co-routines, mainstream, closure и т.д. переводить на русский.
Если ты хочешь писать ОО, возьми ОО-язык, который специально для этого придуман.
Error: ORA-06502: PL/SQL: numeric or value error: character to number conversion error
Наверное, при переводе на Java что-то сломалось
> Половина, если не больше, "причин" весьма и весьма спорны.
> Некоторые вообще неверны.
И кому народ поверит: благородному дону или Контре?
Только твоих взглядов на ФП он совсем не разделяет:
Функциональное программирование прекрасно, оно - радость созерцания. Как только кто-то поймет функциональное программирование, он немедленно перейдет к нему. Массы, которые застряли в устаревшем императивном и объектно-ориентированном программировании, делают это из слепого предубеждения. Они просто не понимают.
Как пример готового проекта, дам ссылку:
http://www.fdc.ru/portal/page?pageid=60,54746&_dad=portal&_schema=PORTAL
это адрес страницы, на которой описывается проект миграции из PL/SQL в современные Java технологии.
Ну надо же, неужели все-таки написали.
Я не понимаю, как этот самый pattern matching поможет в больших проектах, если он работает только со списками.
Сначала пара вопросов:
что такое тип в понятие ML-я?
можно ли на нем описать тип натуральное число? как это будет выглядить? Что это нам даст?
можно ли описать, например, тип C-идентификатор - это строка, с рядом ограничений
можно ли описать, например, тип идентификатор - это нечто, которое можно между собой сравнивать.
Тип в понятии ML - это обычный тип. Ограничения на типы можно накладывать в Haskell.
Выглядит это примерно так: берем и говорим, что всякий, кто претендует на звание целого числа, должен поддерживать операции + и -. После чего, ты создаешь свой тип и реализуешь для него эти операции и дальше твой тип можно передавать в функции, где требуются целые типы. Т.е. если есть полиморфная функция, которая складывает свои аргументы, то в нее можно передать значения любого типа, если они поддерживают понятие целое. На самом деле в Haskell'e можно делать даже больше, но я не помню уже что именно.
> Чтобы понять язык, нужно научиться на нём думать, а не переводить с него на родной и обратно.
Не вижу я этого нового мышления, не вижу. Самое главное не вижу, что мне это новое мышление дает.
Вот с чем я, как архитектор, буду работать в ФЯ? со списками?
Рассмотрим, как человек, в реале взаимодействует с окружающим миром.
В реале, можно взять любой предмет, даже незнакомый, и по этому предмету определить, что он умеет. Подошли к окну, посмотрели на него, потрогали, попинали и поняли, что он состоит из рамы, стекла; умеет показывать, что находится за ним; быстро портится, если по нему пинать и т.д.
В ОО-программе, все тоже самое, я беру незнакомый объект, и прямо по объекту понимаю, что он из себя представляет, что с ним можно делать и т.д.
Для большого проекта - это очень важный принцип, т.к. все держать в голове не получается, особенно, если в разработке участвуют другие люди.
В ФЯ я всего этого не вижу, а так же не вижу какие-то другие подходы, которые позволяют мне также легко работать с большой программой, незнакомой программой.
А также не вижу, что мешает, например, для списка "круг" применить функцию "посмотреть сквозь него", поэтому опять же не понимаю фразу о том, что "если программа на ML скомпилировалась, то в ней нет ошибок".
> Вот ты английские термины часто используешь, почему не переводишь на русский?
Вот скажу я "сборщик" - а каждый поймет, что-то свое (linker, assembler, compilator, builder). Зачем мне это надо?
В OCamle похожего эффекта можно добиться с помощью объектов. В функции там можно передавать любые объекты, если у них есть нужный набор функций с правильными сигнатурами. Т.е. это что-то вроде интерфейсов в C#, только лучше.
Объекты mutable-ные, или нет? Тип объектов можно менять?
> Т.е. это что-то вроде интерфейсов в C#, только лучше.
Чем лучше?
Приведи примеры программ, для которых ОО лишний, и не дает ничего полезного.
ps
Замечу, что список, атом, число, строка, функция - это все тоже объекты, каждый из которых поддерживает свой набор операций над ним.
т.е. получается, что в программе на ФЯ у нас есть только объекты зашитые в язык, но нет возможности описать свои объекты.
а это будет работать если вместо чисел будут комплексные числа?
при чем самих комплексных чисел на уровне языка нет.
и как это для случая комплексных чисел запишется?
Приведи примеры программ, для которых ОО лишний, и не дает ничего полезного.Посмотри в исходники Linux или FreeBSD.
Замечу, что список, атом, число, строка, функция - это все тоже объекты, каждый из которых поддерживает свой набор операций над ним.
т.е. получается, что в программе на ФЯ у нас есть только объекты зашитые в язык, но нет возможности описать свои объекты.
Да, любую задачу можно свести к объектам. Но это вовсе не означает, что именно так и надо делать.
Что ты имеешь ввиду? Объекты могут содержать mutable поля, чтобы не копироваться при каждом присваивании и могут быть целиком объявлены, как mutable, как и любая другая переменная. Тип менять естественно нельзя, и на что его собственно менять?
>Чем лучше?
Лучше тем, что все делается неявно самим компилятором. Кроме того, все это полиморфно.
Там полно ОО
Так что пример плохой.
Допустим сначала был тип круг, а мы к нему применили операцию изменить радиус по осиX, в результате тип должен измениться на тип эллипс.
Естественно.
Вот ты видишь людей, у них, как у тебя, одна голова и две руки.
Но вот их поступков ты не понимаешь, сам признаёшься.
Потому что думают они по-другому.
> Самое главное не вижу, что мне это новое мышление дает.
Это можно понять, только научившись ему.
Пример 1:
fun exchange (a: int, b: int) = (b, a)
эта функция получает пару целых чисел и возвращает понятно чего.
Пример 2:
fun exchange (a: int, b: 'a) = (b, a)
эта функция получает пару, состоящую из целого числа на первом месте и абсолютно чего угодно, хоть целую программу туда запихни, на втором месте. Обозначение tyvar как 'a в данном случае значения не играет, потому что оно встречается только один раз. По-другому то же самое можно записать так: fun exchange (a: int, b) = (b, a)
Пример 3:
fun exchange (a: 'a, b: 'a) = (b, a)
это значит, что функция принимает пару значений одного и того же типа.
Пример 4:
fun exchange (a: 'x, b: 'y) = (b, a или просто fun exchange (a, b) = (b, a)
принимает значения произвольных типов.
Тут я продемонстрировал некоторые примеры параметрического полиморфизма, что в С++ достигается использованием неуклюжих шаблонов.
ML поддерживает несколько встроенных типов, а именно: char, string, int, real, unit (аналок void в С, единственное его значение - это ).
Свои типы данных, в понятиях С++, инстанциированные шаблоны, могут быть такими: int list, int * string, (int * string list) list list и другие, которые создаются на основе чего-то типа декартова произведения типов ( тип1 * тип 2 * ... * типН ) или на основе того же самого инстанциирования, для этого нужно, чтобы тип был соответствующим образом определен.
Пример реализации "Натуральное число":
structure Nat :> sig
(* это что-то типа интерфейса *)
type nat
val make_nat : int -> nat
val to_int : nat -> int
val plus : nat -> nat -> nat
val minus: nat -> nat -> nat
(* тут идут другие декларации *)
end = struct
datatype nat =
Nat of int
| NotNat
fun make_nat n = if n > 0 then Nat n else NotNat
fun to_int (Nat n) = n
| to_int _ = 0
fun plus (Nat n1) (Nat n2) = Nat (n1 + n2)
| plus _ _ = NotNat
fun minus (Nat n1) (Nat n2) = if n1 > n2 then Nat (n1 - n2) else NotNat
| minus _ _ = NotNat
(* тут идут другие функции *)
end
Для окружности:
structure Circle :> sig
type circle
val make_circle: real * real * real -> circle
val relocate : (real * real) -> circle -> circle
val to_graphics : circle -> Graphics.graphics
end = struct
datatype circle =
Circle of real * real * real (* коорд. х, коорд. у, радиус *)
| NotCircle
fun make_circle (x, y, r: real) = if r > 0 then Circle (x, y, r) else NotCircle
fun relocate (x', y') (Circle (x, y, r = Circle (x', y', r)
| relocate _ _ = NotCircle
fun to_graphics (c as (Circle _ = let
fun to_graphics' (c as (Circle (x, y, r graphics alpha = let
val x1 = (x + r * (Math.cos alpha
val y1 = (y + r * (Math.sin alpha
val alpha = alpha + 1;
val x2 = (x + r * (Math.cos alpha
val y2 = (y + r * (Math.sin alpha
val graphics = Graphics.DrawLine x1, y1 (x2, y2 graphics
in
if alpha < 360 then
to_graphics' c graphics alpha
else
graphics
end
| to_graphics' _ g _ = g
in
to_graphics' c Graphics.empty 0
end
| to_graphics _ = Graphics.empty
end
Если что-то непонятно, могу разъяснить.
Там есть Handle-ы плюс функции, которые оперируют с этими handle-ами.
Фактически это тот же ОО, только без syntactic sugar.
Но, почему, я могу обяснить (по крайней мере пытаюсь что дает ООП, а ты не можешь объяснить, что дает ФЯ?
Это тоже "думать по другому"?
Пока стандартный вопрос на засыпку, что возвращает (какой тип) функция "растянуть по оси-x", если ее применить к кругу или эллипсу?
какой тип будет в результате, если к кругу применить последовательно операции: растянуть по осиX в два раза, сжать по осиX в два раза?
А как это выглядит на C# или там С++?
> Посмотри в исходники Linux или FreeBSD.Ну и отлично. Там же не только ОО. Требовались примеры, где ОО лишнее - вот примеры.
Там полно ОО
Так что пример плохой.
Тогда говори точнее. Иначе получается "иди туда не знаю куда, но там где-то точно есть места, где ОО нет".
> Ну и отлично. Там же не только ОО. Требовались примеры, где ОО лишнее - вот примеры.Такие места там сильно преобладают.
Тогда говори точнее. Иначе получается "иди туда не знаю куда, но там где-то точно есть места, где ОО нет".
Средствами языка тоже никак. Потому что C# и C++, напрямую, тоже поддерживают только те операции, которые не выводят тип из поля.
Но на smalltalk-е ("правильный" ОО-язык) это решается, через динамическую типизацию, и изменение типа объекта
вот цитата на эту тему
Рассмотрим класс "эллипс". Этот класс может обозначать две разные сущности:
1. Элиппс, не являющейся кругом. Или другими словами - это эллипс без вырожденных случаев.
2. Эллипс с учетом всех вырожденных случаев. Фактически такой эллипс должен содержать поведение и для таких вырожденных случаев, как круг, точка и может быть такие вырожденные случаи, как "плоскость"(эллипс бесконечного радиуса "отрезок" (эллипс нулевой длины и какой-то ширины "прямая" (эллипс нулевой длины и бесконечного радиуса
"полоса" (эллипс какой-то ширины и бесконечной длины).
Теперь поговорим об интерфейсах для данного случая.
Интерфейсы получаются следующие:
Не вырожденный эллипс ( 0 < ширина < бесконечность, 0 < высота < бесконечность, ширина != высота)
Круг
"Точка"
"Плоскость"
"Полоса"
"Отрезок"
Обобщенный эллипс
Рассмотрим интерфейс "круг". Здесь тоже возможны два разных интерфейса, которые скрываются под одним словом "круг".
С одной стороны - это интерфейс, который поддерживает все операции доступные над кругом, в том числе и растяжение по одной из оси, с другой стороны - возможен интерфейс, который допускает только те операции, результат которых остается в поле "круг" (т.е. в результате после операции круг остается кругом).
рассмотрим интерфейс "обобщенный эллипс".
Например, метод "Дай оси симметрии". такой метод в отличии от такого же метода для невырожденного эллипса, должен возвращать не только пару осей, как для невырожденного эллипса, но и должен уметь возвращать множество из бесконечного числа осей симметрии, т.к. у частного случая "круг" именно такой набор осей симметрии.
Или возьмем метод "Дай больший радиус", если для невырожденного эллипса такой метод имел смысл, то для обобщенного эллипса - этот метод уже может заканчиваться ошибкой, т.к. большего радиуса у вырожденных случаев (круг/точка) просто нет.
Получается, что когда мы говорим про интерфейс "обобщенный эллипс" мы также подразумеваем два разных интерфейса: один интерфейс - это операции, которые доступны над каждым вариантом эллипса, второй интерфейс - это набор операций, которые доступны хотя бы над одним вариантом эллипса.
Компоненты не имеют право, напрямую, ссылаться на типы-реализации (иначе получаем нарушение инкапсуляции и нарушение полиморфизма). Компоненты имеют право только пользоваться вышеприведенными интерфейсами.
Теперь вернемся к наследованию, как я уже вышеупомянул, термин "наследование" в первую очередь означает наследование реализации.
Рассмотрим класс-реализацию "обобщенный эллипс".
Этот класс должен реализовывать все вышеуказанные интерфейсы, а их у нас получилось, как минимум 12 штук.
Т.е. получается, что класс-реализация "обобщенный эллипс" содержит реализации интерфейсов "круг", "точка", "невырожденный эллипс" и т.д.
Рассмотрим класс-реализацию "круг".
Этот класс содержит 3 реализации интерфейсов: класс-реализация "точка", класс-реализация "круг с операциями из поля круг", класс-реализация "круг с операциями, выводящие из поля круг".
Теперь рассмотрим наследование реализаций.
Наследовать мы можем, как класс-реализацию "круг" от класс-реализации "обобщенный эллипс", так и "обобщенный эллипс" от "круга".
-- наследование класса-реализации "обобщенный эллипс" от класса-реализации "круг" --
Здесь все просто. Обобщенный эллипс добавляет реализацию тех методов, которые не были реализованы в круге.
-- Наследование класса-реализации "круг" от класса-реализации "обобщенный эллипс". --
Вот мы и добрались до самого интересного.
В данном случае такое наследование тоже может быть. Класс-реализация "круг" после наследования от класса-реализации "обощенный эллипс" отключает ненужные ему реализации, оставляя только необходимые.
В данном случае, нет нарушений правил Лисковой, потому что внешние компоненты не имеют права ссылаться на класс-реализации, они имеют право только ссылаться на интерфейсы.
А для всех внешних компонентов, данный класс-реализация выглядит как правильная класс-реализация "круг"-а.
И для внешних компонентов такое наследование совершенно прозрачно.
Поговорим чуть-чуть о динамической типизации.
Как мы уже выше убедились, у нас есть интерфейсы, которые оставляют класс в том же поле, что и до операции, но есть также и операции, которые переводят класс из одного поля в другое.
Если программе достаточно интерфейсов первого рода, то можно обойтись статической типизацией, а также тем, что объект не меняет набор поддерживаемых интерфейсов по ходу работы.
Если же нам нужны и интерфейсы второго рода и не достаточно интерфейсов первого рода, то необходимы также и динамической типизации, а также изменение набора поддерживаемых интерфейсов у объекта по ходу работы.
Все текущие индустриальные языки (C++, Java, C#) довольствуются интерфейсами только первого рода, и не поддерживают, напрямую, интерфейсы второго рода.
ФЯ - основываются на теории категорий, которая как раз и предлагает такой взгляд на мир. Теория категорий концентрируется не на объектах-множествах (как теория множеств а на функциональных связях между ними, сущность объектов не играет никакой роли. Это дает новый уровень абстракции.
Приведи законченный пример.
ps
иначе у нас неравноценные позиции, тебе нужно доказать, что в Linux-е есть хоть один кусок куда без ООП, а мне надо доказать, что в Linux-е все(большинство) куски кода использует ООП.
насколько я помню математику, доказать второе в реальный условиях почти невозможно.
Приведи законченный пример.IP и TCP/IP стэк src/sys/netinet в любой BSD. Нет намеков на ОО. Можно посмотреть через CVSweb на www.freebsd.org или www.openbsd.org
лингвистических конструкций в функциональном стиле:
applicative universal grammar.
Там элементы языка типизированы наподобии ФЯ,
например, существительное имеет тип T,
предложение - S, прилагательное - (T->T
глагол (T->S а всякие вспомогательные конструкции
ещё более сложные абстрактные типы.
В результате "белая кошка" это "белый(кошка)", а не "кошка.setColor(белый)".
Таким образом, наделить объект дополнительным свойством можно не
внося изменений в сам объект, что больше соответствует реальному миру,
где объекты остаются одими и теми же, а свойствами их наделяют люди
по мере того, как начинают их различать и придавать им значение.
где это в ФЯ проявляется?
Допустим у меня мяч относится к категории физический предмет (доступно состояние: положение в пространстве, размер; операции: передвинуть, изменить размер и т.д. а также относится к категории товар (у него есть цена, его можно продать и т.д.)
Как это будет записываться на ФЯ?
> Это дает новый уровень абстракции.
Пока это не увидел. В некотором смысле, это те же грани одного объекта, если в терминах ООП.
Приведи, плиз, какой-нибудь пример.
Пока стандартный вопрос на засыпку, что возвращает (какой тип) функция "растянуть по оси-x", если ее применить к кругу или эллипсу?В ML, как и в С (огромный ему за это пинок под его сишный зад) параметры в функции передаются только по значению. В паскале есть такое ключевое слово var, которое означает, что параметр передается не по значению, а по имени (по-моему, это называется так, хотя я могу и ошибаться). В идеале, в функциональном языке ссылок быть не должно, поэтому получать результат через парамерты, и, тем более, но это уже в силу передачи по значению, менять их значения — нельзя. Вообще, __изменить__ в функциональных языках ничего нельзя! (Знатокам ML и ocaml — типы ссылки ('a ref которые все могут очень сильно испортить, по моему мнению просто позволяют схитрить и выйграть время ценой красоты кода. Это мусор в ML, так же, как и исключения. Все это для любителей императива, которые готовы брать МЛ только за его паттерн-мэтчинг).
какой тип будет в результате, если к кругу применить последовательно операции: растянуть по осиX в два раза, сжать по осиX в два раза?
В идеале, от которого ML на самом деле совсем недалеко, результат функция должен возвращаться как значение. Соответственно, функция, которая растягивает или сжимает эллипс должна вернуть новый эллипс, на котором все преобразования проделаны. ML для этого оптимизирован, так что никаких накладок по поводу нерационального использования памяти не возникнет. Более того, это и есть парадигма ФП. Огромнейший плюс ФП как раз в том, что у функции нет никаких сторонних эффектов, то есть, например, в ML, если не использовать фичи императивного подхода, а они там, к сожалению, есть, как я уже говорил выше, можно быть уверенным, что функция ничего не сделает лишнего, только вернет результат. Да даже в случае с использованием МЛ-ных имперетивных референсов, можно быть уверенным за нереференсные переменные, значение которых можно "изменить", только переопределив их в их области видимости или во вложенной.
Функции ввода-вывода реализованы на очень низком уровне и являются скорее не частью библиотеки, а частью языка. На самом деле, это, как посмотреть, но смотреть именно так очень выгодно. Как результат своей работы, они возвращают unit. Можно, конечно, написать функцию вычисления факториала, которая попутно бы срала мегабайтами на диск ЦЭ или меняла бы файлы в system32, но это будет не ошибка программиста, а банальное хулиганство. Язык от хулиганов страховать не должен. Это задача ОС. В остальном, криворукие программисты просто не смогут скомпилировать код до тех пор, пока не поймут суть проблемы.
------
вот еще одна ссылка, должно быть, рабочая:
http://www.fors.biz/fors/docs/english/p-j_engl.html?view=default_english
Никак. Поскольку совершенно неважно какими свойствами обладает объект сам по себе. Нам интересно, как он взаимодействует с другими объектами. Мяч, например, можно пнуть. Получаем два объекта - нога и мяч. К какому из них должно относиться действие "пнуть"? К обоим - нельзя, лишняя функциональность, а если выбрать только один, то возникает вопрос, а почему не другой? Кроме того, мяч можно не только пнуть, но и кинуть, покатить и т.п. А ногой можно не только пинать, но и шагать, прыгать. И как здесь применить ООП?
В ФЯ мы бы не мучались, а определили бы полиморфную функцию пинать с двумя аргументами.
запятую лучше, вообще, стараться не использовать, потому что она приводит к довольно трудновидимым глюкам
тогда надо так:
int f1[3];
int f2[3];
f1[0]=1;
f1[1]=4;
f[2]=2;
f2[0]=3;
f2[1]= -4;
f2[2]=1;
ни одной запятой
В больших приложения за такое убивают.
а если везде std:: писать, то в больших проектах от этого сами помирают
если боишься пересечений - пиши using внутри функции.
тем более данной кусок изменяет семантику оператора запятая
угу, есть такой недостаток. Но как я понял, ты этим оператором и так не пользуешься.
void Пнуть (Мяч, НогаВБутце){}
void Пнуть (Шкаф, ГолаяНога){}
Но вот что делать с динамическими типами?
объяснение, откуда берутся динамические типы, я уже приводил вверху.
И тем не менее, на них можно делать императивщину,
причём иногда достаточно эффективно.
"Imperative Functional Programming", Wadler, Jones, 1993
http://www.research.microsoft.com/Users/simonpj/Papers/imperative.ps.Z
Приведи, плиз, какой-нибудь пример.
Я могу сказать только, что при использовании теории категорий существенно упрощается описание свойств алгебраических объектов. Там как раз неважно, что за группа, кольцо, модуль конкретно изучаются, важны некоторые общие свойства присущие некоторому классу объектов.
>объяснение, откуда берутся динамические типы, я уже приводил вверху.
ХЗ, что с ними делать. Нужен конкретный пример.
Например, когда я писал компилятор, то в результате парсинга получалось дерево с различными видами вершин. Реализовывать каждый вид вершины отдельной структурой - нерационально, поскольку структура дерева будет повторена аж 3 раза - в парсере, в иерархии структур для вершин и функциях обходящих дерево, в то время, как достаточно одного парсера. В случае изменения дерева изменения пришлось бы вносить в 3 местах. Поскольку в SML строгая типизация и нет никаких динамических типов, то единственный вариант был их сымитировать, т.е. в каждой вершине хранить список подвершин с идентификаторами типов. В результате функция, которая обрабатывала дерево занимала несколько строчек, я ей передавал аргументы-функции, которые реагировали только на нужные вершины. Было довольно удобно.
можно даже написать "универсальное дерево", что-то типа XML-дерева, и пихать туда все что угодно. Продолжая таким образом, можно остановиться только на "правильном" заполнении дерева при имеющихся универсальных функциях его обхода. Но в этом случае сам процесс "правильного" заполнения может стать гораздо сложнее программирования обхода или изменения каких-то небольших участков кода. Золотая середина? Нет золотой середины. Это коммерческая уловка. Но мозги есть всегда. Написать с первого раза правильное дерево настолько же важно, как и придумать схему данных в СУБД. А можно и блобами, как написано в соседнем треде. Прелесть МЛя и в том, что при любой сложности очень легко вносить изменения в проект. Только бы не было императивщины.
Ты прав. У меня и было "универсальное" дерево с однотипными вершинами. Проблема с ним в том, что трудно обеспечить правильный порядок подвершин в списке и сам доступ к ним затруднен, поскольку нужно перебирать все подвершины, чтобы найти нужную. Но, я считаю, что такие неудобства полностью компенсировались простотой обхода дерева. К тому же, эти неудобства можно было бы уменьшить, а то и вообще скрыть автоматической генерацией кода по псевдоязыку проверки и модификации дерева. К сожалению, когда мне пришла в голову эта идея, я уже уволился с этого проекта.
openjade
об устройстве естественных языков и возможности их обработки.
Знание ООП мне в этом плане практически ничего не дало,
разные мелочи возможной реализации нижнего уровня
просто не позволяли увидеть, как всё красиво и естественно
может получиться сверху. Причём в первую очередь,
именно из-за запихивания свойств внутрь объектов,
как предписывает ОО подход.
Интересно, как в ABBYY с ЕЯ работают?
Интересно, как в ABBYY с ЕЯ работают?Будь осторожнее, а то за могут и покарать.
А почему интересно программисты АББИИ поставлены в ФАКе на один уровень с Биллом Гейтсом?
Для реализации концепции автоматического динамического распараллеливания программ в Т-системе предложена новая модель организации вычислительного процесса, основанная на следующих базовых принципах:
В качестве основной парадигмы рассматривается функциональное программирование. Программа представляет собой набор чистых (без побочных эффектов) функций. Каждая функция может иметь несколько аргументов и несколько результатов. В то же время тела функций могут быть описаны в императивном стиле (на языках типа FORTRAN, C и т.п.) Важно только, чтобы:
Всю информацию извне функция получала только через свои аргументы;
Вся информация из функции передавалась только через явно описанные результаты.
По этой ссылке подробнее о том что наверху:
http://www.ctc.msiu.ru/program/t-system/first/node15.html
По этой коммерческое применение:
http://skif.pereslavl.ru/skif/
Ксати люди принимающие участие в разработке этой системы работают и преподают на мехмате.
Тут есть вот такая проблема: как быть, если компилятор обнаруживает, что у него есть несколько путей преобразования?
Возьмем те же коллекции.
Допустим у нас есть коллекция с индексным доступом (например, коллекция, которая находится в памяти и коллекция с последовательным доступом (например, коллекция, которая находится на диске).
Для коллекция с индексным доступом также доступен последовательный доступ.
Далее мы можем написать преобразование, которое обеспечивает индексный доступ к последовательной коллекции
Так же у нас есть две сортировки: одна для последовательного доступа, другая - для индексного доступа.
Внимание, вопрос:
Какую сортировку и почему должен выбрать компилятор, если в функцию сортировки передается коллекция, которая поддерживает и индексный доступ, и последовательный доступ?
А вот это что?
int in_broadcast(struct in_addr, struct ifnet *);Все тоже самое. Есть структура, и есть операции над этой структурой - фактически опять же объявляем объект.
int in_canforward(struct in_addr);
int in_cksum(struct mbuf *, int);
int in4_cksum(struct mbuf *, u_int8_t, int, int);
void in_delayed_cksum(struct mbuf *);
int in_localaddr(struct in_addr);
void in_socktrim(struct sockaddr_in *);
char *inet_ntoa(struct in_addr);
P.S. Особенно мне прет считать struct in_addr объектом
public class IPAddress
{
private static IPAddress;
public IPAddress(byte[] address);
public IPAddress(byte[] address, long scopeid);
internal IPAddress(int newAddress);
public IPAddress(long newAddress);
public override bool Equals(object comparand);
public byte[] GetAddressBytes;
public override int GetHashCode;
public static short HostToNetworkOrder(short host);
public static int HostToNetworkOrder(int host);
public static long HostToNetworkOrder(long host);
internal bool IsIPv4Compatible;
internal bool IsIPv4Mapped;
internal bool IsIPv6LinkLocal;
internal bool IsIPv6Multicast;
internal bool IsIPv6MulticastGlobal;
internal bool IsIPv6MulticastLinkLocal;
internal bool IsIPv6MulticastNodeLocal;
internal bool IsIPv6MulticastOrgLocal;
internal bool IsIPv6MulticastSiteLocal;
internal bool IsIPv6SiteLocal;
public static bool IsLoopback(IPAddress address);
public static short NetworkToHostOrder(short network);
public static int NetworkToHostOrder(int network);
public static long NetworkToHostOrder(long network);
public static IPAddress Parse(string ipString);
public override string ToString;
public long Address { get; set; }
public AddressFamily AddressFamily { get; }
internal bool IsBroadcast { get; }
public long ScopeId { get; set; }
public static readonly IPAddress Any;
public static readonly IPAddress Broadcast;
internal const string InaddrNoneString = "255.255.255.255";
internal const string InaddrNoneStringHex = "0xff.0xff.0xff.0xff";
internal const string InaddrNoneStringOct = "0377.0377.0377.0377";
public static readonly IPAddress IPv6Any;
public static readonly IPAddress IPv6Loopback;
public static readonly IPAddress IPv6None;
public static readonly IPAddress Loopback;
internal const long LoopbackMask = 127;
internal long m_Address;
private AddressFamily m_Family;
private int m_HashCode;
private ushort[] m_Numbers;
private long m_ScopeId;
public static readonly IPAddress None;
internal const int NumberOfLabels = 8;
private static bool s_Initialized;
}
Отличия только в синтаксисе вызовов.
И чем этот In_Addr Отличается от вот такого класса: ?Сделай diff:
struct in_addr {
in_addr_t s_addr;
};
но явное объявление объектов дает нам полезный Syntactic shugar, который очень удобен при разработке больших программ, т.к. позволяет сразу определить какие операции и свойства доступны для данного объекта в данный момент.
struct in_addr
{
in_addr_t s_addr;
};
char *inet_ntoa(struct in_addr);
от
class in_addr
{
public:
in_addr_t s_addr;
char *inet_ntoa;
};
?
Вызовы
inet_ntoa(in_addr);
и
in_addr.inet_ntoa;
даже один и тот же asm-код генерируют.
Да. Нужно просто уметь это видеть.
То же самое относится и к чистым функциям.
Суслик есть.
> но явное объявление объектов дает нам полезный Syntactic shugar,
> который очень удобен при разработке больших программ,
> т.к. позволяет сразу определить какие операции и свойства
> доступны для данного объекта в данный момент.
"Удобство" - это не более чем согласованность
со стереотипами и прочими окаменелостями в мозгах.
Удобство ограничивает возможности, а не расширяет их.
"Полезность" для "больших" проектов весьма спорна.
Ты прочитал lisp success story? Почему там так получилось?
Согласен.
> То же самое относится и к чистым функциям.
Тоже согласен.
Архитектура (каркас приложения) - это в первую очередь объекты/сущности. Попробуйте описать хоть что-нибудь не используя ни одного существительного, а ведь в речи, как раз существительное указывает на наличие явного или не явного объекта.
Поэтому я и говорю, что объекты именно в большом проекте полезны, потому что позволяют видеть архитектуру приложения явно в коде.
Функции - это преобразование данных/объектов.
> "Удобство" - это не более чем согласованность
> со стереотипами и прочими окаменелостями в мозгах.
> Удобство ограничивает возможности, а не расширяет их.
Совсем не согласен.
Удобство позволяет сосредоточится на главном, держать в памяти действительно важные вещи, позволяет уменьшить время разработки, уменьшить кол-во ошибок, в конце концов, уменьшить стоимость разработки.
Только если ты так захочешь.
> Попробуйте описать хоть что-нибудь не используя ни одного существительного, а ведь в речи,
> как раз существительное указывает на наличие явного или не явного объекта.
В языке процесс может выражаться как глаголом, так и существительным (номинализация).
В ФП функции являются данными, что позволяет рассматривать их не только как процессы,
но и как сущности. Процедурное программирование и ООП стараются игнорировать эту особенность.
> Функции - это преобразование данных/объектов.
... и кроме того, сами могут являться данными.
Свойства, принадлежащие якобы объектам, отражают лишь наше восприятие этих объектов.
Всё, что мы знаем про объекты, является результатом нашего взаимодействия с ними,
т.е. наблюдений. Приписывать это самим объектам - лишь один из способов мышления,
очень часто к тому же приводящий к парадоксам.
Теперь - о мутабельности.
Это тоже отражает наше восприятие времени, которое, как известно из физики, относительно.
Всё, что мы знаем об объектах - результаты наблюдений, т.е. данные.
Причём они в некотором смысле, вечны - мы можем про них забыть (как GC в ФЯ
но можем и хранить столько, сколько захотим.
Данные, полученные "только что", принципиально ничем не хуже данных, полученных "давно".
Объявление какого-то из возможных наборов данных "текущим состоянием" объекта, как это
нужно делать в ООП, субъективно.
Даже SQL-базы, приводимые в качестве образца императивности, поддерживают конкурентные
транзакции, в каждой из которых наблюдаемое состояние - своё.
> Удобство позволяет сосредоточится на главном, держать в памяти действительно важные вещи,
> позволяет уменьшить время разработки, уменьшить кол-во ошибок, в конце концов, уменьшить стоимость разработки.
Инвалиду костыль позволяет быстрее и надёжнее ходить, и является для него удобством.
Но вот здоровому человеку он только мешает при ходьбе.
Архитектура - это не объекты, а взаимосвязи между ними (Аристотель vs Гегель ).
Собственно, в этом и состоит основное отличие
ООП ("всё является объектом") от ФП ("всё является функцией").
> Попробуйте описать хоть что-нибудь не используя ни одного существительного,
> а ведь в речи, как раз существительное указывает на наличие явного или не явного объекта.
Именно на наличие. А все "свойства" у такого объекта снаружи и не имеют значения,
пока явно где-нибудь не понадобятся.
> Поэтому я и говорю, что объекты именно в большом проекте полезны,
> потому что позволяют видеть архитектуру приложения явно в коде.
Взаимосвязи между объектами видеть тяжело.
> Функции - это преобразование данных/объектов.
Функция - это в первую очередь не пребразование,
а некоторое отношение между сущностями.
Причём до некоторого момента может не иметь значения,
как именно это отношение и сами сущности должны
быть устроены внутри.
>> "Удобство" - это не более чем согласованность
>> со стереотипами и прочими окаменелостями в мозгах.
>> Удобство ограничивает возможности, а не расширяет их.
>
> Совсем не согласен.
> Удобство позволяет сосредоточится на главном,
> держать в памяти действительно важные вещи,
> позволяет уменьшить время разработки, уменьшить
> кол-во ошибок, в конце концов, уменьшить стоимость
> разработки.
Я не вижу здесь противоречия.
Если инструмент и подход выбраны правильно, то так оно и есть.
Только вот далеко не всегда так бывает, и "действительно важных
вещей" может только прибавиться из-за того, что удобство есть
совсем не там, где надо.
но и как сущности. Процедурное программирование и ООП стараются игнорировать эту особенность.
Что в том же C-и нельзя сделать такого с функцией, что можно сделать с обычными данными?
ps
Хотя да, у C-и есть недостаток - функция не может быть объявлена внутри другой функции.
Но возьмем тот же SmallTalk - самый объектный язык, там то чего нет?
> Инвалиду костыль позволяет быстрее и надёжнее ходить, и является для него удобством.
> Но вот здоровому человеку он только мешает при ходьбе.
Почему ты взял именно костыль? Почему ты не взял для примера: велосипед, машину и т.д.?
Несмотря на то, что он "самый объектный", в вашем мейнстриме он не котируется, так?
Вряд ли потому, что там чего-то нет (про это я ничего сказать не могу, почти ничего не знаю о нём).
В моём понимании, он довольно похож на лисп, только структуры данных и синтаксис менее регулярные.
> Почему ты взял именно костыль? Почему ты не взял для примера: велосипед, машину и т.д.?
Кому нужен костыль, велосипед не поможет, и машина ему подойдёт только специальная.
Мышление же более пластично, ограничение сознания можно преодолеть,
гораздо легче, чем ампутацию ноги, например.
пока явно где-нибудь не понадобятся.
Состояние таких наружных свойств где хранится?
Да, в "нашем" mainstream-е не котируется. В целом, опять же по тем же причинам: тяжело стыковать с этим самым MainStream-ом.
Но есть мнение, что Smalltalk рулит как раз в написании сложных системах с большим кол-вом часто меняющихся требований.
"Ноги" многих методик и подходов растут именно из smalltalk-а
> Вряд ли потому, что там чего-то нет (про это я ничего сказать не могу, почти ничего не знаю о нём).
В Smalltalk-е нет статической типизации. Причем совсем нет - это мешает выжимать соки из производительности, т.к. даже критические к производительности части кода сложно оптимизировать на нативном уровне.
> В моём понимании, он довольно похож на лисп, только структуры данных и синтаксис менее регулярные.
имхо, в Smalltalk-е только два основных понятия - это объект и сообщение.
ВызовыНу и что? Это ж не доказывает, что структура обладает всеми св-вами объекта.
inet_ntoa(in_addr);
и
in_addr.inet_ntoa;
даже один и тот же asm-код генерируют.
Именно так принято отображать ОО на Си, если ты не знал
Какое значение вернула transform (где, кстати, оно?)?
А должна вернуть vector.
Именно вот так:
let vec_res = map (+) vec1 vec2; (* SML *)
(set! vec-res (map + vec1 vec2 ;; Scheme
и никак иначе.
То есть, вектор должен быть вовзращён _значением_,
а не побочным эффектом.
Все ваши хвалёные transform --- это Фортран, только вид с другой стороны.
Опять же, как ты напишешь (fold + 0 (map * x y?
(Примечание: определение fold по SRFI-1.)
---
...Я работаю антинаучным аферистом...
Плюс можно переопределить.
Пущай сразу пары чисел складывает.
(define old-+ +)
(define (+ a b)
(cond number? a) (+ (cons a 0) b
number? b) (+ a (cons b 0
(else (cons (old-+ (car a) (car b (old-+ (cdr acdr b
---
...Я работаю антинаучным аферистом...
Ты будешь работать с тем, что хочешь видеть.
Знаком с понятием "фрейм?"
Это появилось давно.
Так что и ООП можно считать лисповским изобретением.
Само ООП очень ограничено по определению, поскольку невозможно работать уже с двумя _равноправными_ объектами.
А если их три?
Или четыре?
Число Ингве, всё-таки, не равно одному.
Собственно, и "compiler," и "linker," и "assembler" представляют собой одно и тоже --- сборщик.
Только эти сборщики работают на разных уровнях.
---
...Я работаю антинаучным аферистом...
Радикальный подход.
Обычно под ФП понимают более мягкое "аппликативное" программирование.
Комбинаторные замашки навроде Хаскелла --- более новая ветка.
---
...Я работаю антинаучным аферистом...
> а ведь в речи, как раз существительное указывает на наличие явного или не явного объекта.
А глаголы появились в речи раньше существительных.
Ты этого не знал, верно?
Трудность ОО подхода заключается в том, что делается попытка описать действия почти исключительно с помощью простых предложений с жёстко выделенным единственным подлежащим.
Что делать, когда подлежащих должно быть два или три, не говорится.
Как будто и не бывает такого.
И оттого имеем суровое ограничение на мощность выражений.
---
...Я работаю антинаучным аферистом...
---
...Я работаю антинаучным аферистом...
> Ты этого не знал, верно?
AFAIK, ты ошибаешься. Глаголы являются более высокой абстракцией, чем существительные.
У детей, например, сначала появляются существительные, и только потом глаголы.
> Трудность ОО подхода заключается в том, что делается попытка описать действия почти исключительно с помощью простых предложений с жёстко выделенным единственным подлежащим.
Приведи пример таких трудностей.
Напрямую ты функцию и на лиспе не создашь, ты ее сможешь только собрать из кусочков.
Но из кусочков можно собрать функцию и в C++.
Пример, плиз.
template<typename Function, typename Result, typename Source>
Result fold(Function function, Result initialValue, list<Source> items)
{
Result res = initialValue;
foreach (Source item in items)
{
res = function(res, item);
}
return res;
}
Соответственно, например, transform через fold:
template<typename Result, typename Function, typename Source>
list<Result> transform(Function function, std::list<Source> items)
{
return fold(delegate(list<Result> items, Source item) {items.add(function(item;},
new list<Result> items);
}
>> Ты этого не знал, верно?
> AFAIK
Плохо знаешь.
Самые первые, самые древние слова --- глаголы повелительного наклонения.
> У детей, например, сначала появляются существительные,
> и только потом глаголы.
Угу. Хорошее такое существительное --- "дай!"
> Приведи пример таких трудностей.
Любое действие наподобие сложения, сцепления.
Тот же "+" для комплексных чисел, умножение для разных там матриц, сцепление и слияние списков и проч.
---
...Я работаю антинаучным аферистом...
у ФП мощность языковых средств не выше, чем у нормального объектно-ориентированного императивного языка, т.к. единственная разница - это замена цикла хвостовой рекурсией.
Единственный плюс ФП - это возможность за пять копеек написать простой транслятор (интерпретатор, компилятор) для функционального языка. Этот плюс достигается за счет принудительного убеднения синтаксиса языка.
> Самые первые, самые древние слова --- глаголы повелительного наклонения.
До этого появляются местоимения. Такое местоимение может заменяться тыканьем пальца. Если пальцев нет, то местоимение звучит в явном виде.
> Тот же "+" для комплексных чисел, умножение для разных там матриц, сцепление и слияние списков и проч.
см. понятие functor.
Ты не можешь объявить функцию литералом.
Ты даже не можешь написать (fold + чего-то-там).
Потому что "+" --- это якобы не функция.
Тебе придётся делать затычку наподобие "int plus(int a, int b) {return a+b;}"
В Лиспе функция --- это список.
В Схеме функция --- это то, что получается от вычисления списка для лиспа.
В Сях "функция" --- это указатель.
Только ты не сможешь разместить в памяти ничего полезного,
если не поработаешь компилятором.
---
...Я работаю антинаучным аферистом...
т.к. мощность языковых средств у ФП не выше, чем у ОО ИП, то значит ФП никакого рывка не может дать в принципе по сравнению с ИЯ.
Замечу, что у декларативных языков, как раз такой потенциал есть, есть надежда, что при накоплении определенной базы декларативный подход позволит резко прыгнуть в разработке ПО.
> чем у нормального объектно-ориентированного императивного языка,
> т.к. единственная разница - это замена цикла хвостовой рекурсией.
ОО можно убрать без всяких ограничений.
> Единственный плюс ФП - это возможность за пять копеек написать
> простой транслятор (интерпретатор, компилятор)
> для функционального языка.
> Этот плюс достигается за счет принудительного убеднения синтаксиса языка.
Это ты только про Лисп или про все?
Тебе уже указали вещи, которые на порядок сложнее для ИЯ, чем для ФЯ: доказательство корректности, распараллеливание, оптимизация.
---
...Я работаю антинаучным аферистом...
Но относительно глаголов повелительного наклонения я прав.
Можешь исследовать этот вопрос.
---
...Я работаю антинаучным аферистом...
> то значит ФП никакого рывка не может дать в принципе по сравнению с ИЯ.
Есть такие слова "семантический разрыв."
Мышление куда более привязано к глаголу, как это ни странно, чем к предметам.
Любой человек пытает машину словом "дай" и "делай,"
то есть описывает _действие_.
Одно только это показывает, что ОО --- это переворачивание с ног на голову.
Если ещё не обратил внимания, то посмотри,
что "сложить произведения пар", через "(fold 0 (map + x y" описывается куда более естественно, чем через
for(i=0,a=0; i<чего-то-там; i++) a+=x[i]*y[i];
> Замечу, что у декларативных языков, как раз такой потенциал есть,
> есть надежда, что при накоплении определенной базы
> декларативный подход позволит резко прыгнуть в разработке ПО.
Декларативные --- это какие?
ФЯ тоже некоторыми относятся к декларативным.
---
...Я работаю антинаучным аферистом...
Это синтаксис. Можно сделать, например, простой препроцессор, который все плюсы будет заменять на создание объекта plus.
в ФЯ тоже нельзя многое записать на уровне синтаксиса: взять те же самые структуры.
> Тебе придётся делать затычку наподобие "int plus(int a, int b) {return a+b;}"
ну, написал я один раз
template<class Left, class Right, class Result>
class plus
{
Result operator (Left left, Right right) {return Left + Right;}
}
вынес это в H-файл, и далее это код везде использую
и где обещанное тобой сильное ограничение?
> В Сях "функция" --- это указатель.
> Только ты не сможешь разместить в памяти ничего полезного,
> если не поработаешь компилятором.
Про функторы уже прочитал?
Покажи мне где в определении функтора встречается хоть раз слово "указатель".
Это те языки, в которых говорится, что надо сделать, а не как.
SQL - можно считать (с оговорками) за пример такого языка.
> ФЯ тоже некоторыми относятся к декларативным.
Я уже в рамках данного треда говорил каким боком ФЯ относится к декларативным языкам.
Нельзя, потому что в ИЯ нет понятия "список".
И данное понятие можно ввести в язык только при помощи объектов.
Что ты понимаешь под "структурами?"
Синтаксис, кстати, не такая уж маловажная вещь.
Он тоже немало влияет.
Именно потому, кстати, в Схеме отказались от имён вроде "quotient" в пользу более привычного "/".
Под функторами мы, видимо, понимаем разные вещи.
Что ты имеешь в виду?
В Сях, например, ты не сможешь сделать простой и понятной любому "шептателю" вещи: считать выражение с консоли (с диска и т.п.) и сделать функцию.
---
...Я работаю антинаучным аферистом...
То есть, можно и Лисп урезать до такой степени.
---
...Я работаю антинаучным аферистом...
А обобщённый, абстрактный?
---
...Я работаю антинаучным аферистом...
>в ФЯ тоже нельзя многое записать на уровне синтаксиса: взять те же самые структуры.
Ибо в ФЯ есть структуры. В OCamle есть все типы данных (если не считать различные типы целых чисел и т.п. муру что есть в С++.
Еще раз повторю свою основную мысль:
у ФП мощность языковых средств не выше, чем у нормального объектно-ориентированного императивного языка, т.к. единственная разница - это замена цикла хвостовой рекурсией.
Единственный плюс ФП - это возможность за пять копеек написать простой транслятор (интерпретатор, компилятор) для функционального языка. Этот плюс достигается за счет принудительного убеднения синтаксиса языка.
Ну это совсем что-то странное. Рекурсия лишь одно из видимых невооруженным взглядом отличий в подходах, а вовсе не единственная разница. Никакого убеднения синтаксиса в принципе нет, ибо изначально для самого простого ФЯ требуется меньше ключевых слов и конструкций, чем для самого простого императивного. Потом языки усложняются и становятся все более монструозными, но их монструозность вовсе не означает качество. Взять, например, С++ и C# или Java.
Мне лень искать ссылки.
Но относительно глаголов повелительного наклонения я прав.
Можешь исследовать этот вопрос.
Извини, если ты настаиваешь на весьма спорном утверждении, то приводить ссылки твоё дело, а не оппонента. Иначе Ваш славный спор превратится в флейм.
Голословное утверждение.
Факты или примеры будут?
> но их монструозность вовсе не означает качество
Опять голословное утверждение.
Где обоснование данного утверждения?
Я не вижу смысла приводить факты и примеры, пока ты не познакомился хотя бы с одним из языков, о которых идет речь - ML, OCaml, Haskell. По всем есть книги (и)или мануалы.
Даже так ---- получение значения.
В частности, разница отразилась в требовании вовзращения значения функцией map.
И в названии "map," а не "transform."
Для того, чтобы понять независимость от рекурсии,
достаточно взглянуть на комбинаторное представление и на FP от Бекуса.
Замечание по поводу лингвистики (больше Глебу, чем тебе, но всё же).
Вопрос о глаголах повелительного наклонения прямого отношения к программированию не имеет, хотя любопытен сам по себе.
Достаточно того, что к машине обращаются именно "сделай" или "дай,"
что получило отражение в ранних ЯП именно императивного направления.
---
...Я работаю антинаучным аферистом...
Господа, о чем спор ? И С++ и Haskell - алгоритмически полные языки. Когда встает вопрос о том, что в некоторых случаях один из языков _выразительнее_ другого, бессмысленно объяснять, как можно добиться того же на другом. И на С++ можно написать интерпретатор Haskell-я и наоборот, это само собой разумеется. Просто надо понимать, что для языка естественно, а что нет.
* В Haskell-е, например, очень удобно частичное применение (partial application то есть (<5) - это одноместный предикат, а (2-) - одноместная функция. Их можно передать параметром в какую нибудь функцию высшего порядка, filter, map, ...
* В Haskell-е есть понятие class (ничего общего с ООП с помощью которого пишутся, например, мультиметоды (функции, полиморфные по нескольким параметрам которых в ООП до сих пор нет.
* В Haskell-е очень мощная система типов. Помимо математической красоты она еще позволяет практически избежать отладки. Это большая редкость, когда правильно стипизированная программа на Haskell-е неправильно работает.
@ В Haskell-е _ужасно_ неудобно выражать понятие состояния. Монады и прочие безумства, Монадный ввод/вывод.
@ В Haskell-е весь полиморфизм статический. Точный тип любой переменной известен на момент компиляции.
...
В общем, надеюсь что вопрос "что лучше" исчерпан.
Более правильно по моему ставить вопрос так: должен ли ООП программист вообще знать о существовании ФП и сопутствующих идей (lambda-исчисление, curring, монады, ленивые вычисления, функции высшего порядка, унификация, ...) Я думаю, что _программист_ (не _кодер_) должен, иначе он просто будет изобретать велосипед.
Всем спасибо за внимание
В идеале, в функциональном языке ссылок быть не должно, поэтому получать результат через парамерты, и, тем более, но это уже в силу передачи по значению, менять их значения — нельзя. Вообще, __изменить__ в функциональных языках ничего нельзя!
Что-то это ты погорячился. В функциональных языках , действительно, ничего нельзя изменить, но это как раз означает, что параметры _всегда_ передаются по ссылке (в том числе в конструкторы). Так дешевле, а волноваться, что кто-то что-то испортит, не надо. Хотя в принципе, что всего лишь вопрос терминологии...
PS. Ну вот я и добрался до середины треда, скоро до начала дочитаю
мультиметоды (функции, полиморфные по нескольким параметрам
@ В Haskell-е весь полиморфизм статический. Точный тип любой переменной известен на момент компиляции.
тогда этот "полиморфизм" - то же самое, что перегрузка в C++. И не понятно, почему тогда
в ООП до сих пор нет
такой чуши гораздо меньше будет
ты б хоть пояснил, кто чушь написал?.. Я или
Тот, кто не изучил вопрос
тогда этот "полиморфизм" - то же самое, что перегрузка в C++.
Очень даже справедливо! Но есть одно НО. Ты подсознательно имеешь в виду заодно и C++-ные шаблоны.
double f(double x, double y){ return(x+y); }
int f(int x, int y){ return(x-y); }
double f(double x, int y){ return(x*y); }
template<class T, class S>
T g(T t, S s){ return(f(f(t,ss; }
g(5, 6);
g(5.5, 6.6);
А в ООП, как таковом, шаблоны пока не очень-то распространены (Java, .NET, ...). Ну, когда-то наверное, дождемся
Да и вообще, Haskell-ный class - это очень глубокая вещь, а мультиметоды так, для примера...
Какой в баню Ленчик! Я это, ессессенно...
Тот кто не изучил вопрос
Так кто не изучил и какой вопрос?
Ты хочешь сказать, что существуют прикладные задачи,
для решения которых, удобно использовать что-то из этих двух?
---
...Я работаю антинаучным аферистом...
О да! Машины Тьюринга в чистом виде используются в шифровании. То есть, если мне нужно скрыть некое "ноу-хау", то сначала пишу нормальную версию, а потом секретную часть переписываю в виде машины Тьюринга
Ты на
code:
let vec_res = map (+) vec1 vec2; (* SML *)
map :: (a->b) -> [a] -> [b]
или как там на SML
map : ('a->'b) -> 'a list -> 'b list
так что ли...
Короче, ею нельзя два массива сложить...
То что тебе надо на Haskell-е называется zipWith, а на SML не знаю как.
"сложить произведения пар", через "(fold 0 (map + x y" описывается куда более естественно, чем через ...
а произведение пар складывать надо вот так
foldl (+) 0 (zipWith (*) x y)
PS Чего вы его вообще слушаете, он про SML неделю назад узнал и теперь брызжит слюной направо налево, машину Тьюринга еще вспомнил...
(map + x y z) ;; для сложения векторов
(apply + (map * x y ;; для сложения попарных произведений
В общем, МЛы мне не понравились.
Я скобки люблю.
---
"...но и без чтения мы разбирались в том,
в каком идти, в каком сражаться стане."
(map + x y z) ;; для сложения векторов
(apply + (map * x y ;; для сложения попарных произведений
Ты на каком языке пишешь-то?
---
...Я работаю антинаучным аферистом...
Схеме умеешь программировать! Все работает
Ааа... , ну, извини. На Честно говоря, я не понимаю приверженцев "общего шёпота."
Но вот, когда на свет появится это, я, возможно, поползу туда.
#f и #t писать неудобно.
Так же, как и lambda.
Либо дело дойдёт до грязного хака с вовзращением T и NIL.
---
...Я работаю антинаучным аферистом...
Я принципиально против Lisp-ов из-за того, что они нетипизированные. А в остальном они может и хорошие...
"Any lisp hacker will tell you there is only one type: a list." (c) Alan Cox
А весь мир "является" ли строго типизированным?
А весь мир "является" ли строго типизированным?
То, что в Lisp-е нет типизации, не означает, что программисты ее не соблюдают. Просто вынуждены держать это в голове, вместо того, чтобы возложить на компилятор.
"Any lisp hacker will tell you there is only one type: a list." (c) Alan Cox
Вот это он молодец, но, если даже забыть про числа и прочие примитивы, то в любом случае еще есть функции, и функции, чьими параметрами являются функции и т.д...
---
...Я работаю антинаучным аферистом...
То, что ты называешь типизацией,--- это только один из её видов,
строгая статическая типизация.
---
...Я работаю антинаучным аферистом...
Ты уже представил, как Бог (а в этом случае он может быть только один) компилирует мир?
make world
О великий гуру!
Типизация возможна только в мире, состоящем из объектов.
Так ли это "на самом деле"?
да, что-то загнался я
Кстати, ты знаком с понятием "динамическая типизация?"
А зачем нужна динамическая типизация?
Ну, вроде как без нее никуда, нельзя же складывать функции или те же списки... Так что либо в статике выясняется, что этот плюс будет складывать числа, либо в динамике.
Я вот как раз за статику. SML компиляторы, например, на основе информации полученной из типизации программы проводят неплохую оптимизацию, хвостовую рекурсию в циклы сворачивают, например. Хотя не знаю, связано это как нибудь с типизацией вообще ...
Основной тезис такой. Типизация программы, это одновременно формулировка и доказательство некоторого утверждения о программе, которое компилятору самому вывести трудновато. Этим утверждением можно пользоваться при оптимизации, справедливость доказательства можно автоматически проверять - и то и другое есть хорошо. Возьмем какую-нибудь Java программу и объявим все переменные Object, а там где методы надо вызывать или к полям доступ, там dynamic_cast-ов навворачиваем. Затормозит неподетски (как пишется-то? не-по-детски? непо-детски? не по-детски, что ли?..). Это не потому, что в Java-е плохой JIT поставили, а просто трудно это...
Да и вообще функциональные языки сложно отлаживать (кто-нибудь видел debugger? без типизации подавно.
А весь мир "является" ли строго типизированным?
Я склоняюсь к тому, что миром правят не свойства, а отношения. Так что язык программирования, подходящий под мою философию, пока вообще не изобрели
Если не совсем понятно, я пару примеров приведу: смотрим в .NET, читаем
virtual Object Clone;
virtual bool Equals(Object o);
В обеих строчках написано одно, а подразымевается другое! Clone пораждает не Object, а нечто, того же типа, что и this, это как бы Haskell-ное "::a->a" а пишут "::a->b", потому что это на языке ООП типизации невыразимо. Все от того, что надо записать некоторое утверждение о паре объектов (= отношение).
make world clean !
...
> хвостовую рекурсию в циклы сворачивают, например.
"Scheme is... properly tail-recursive..."
> Да и вообще функциональные языки сложно отлаживать
Писать надо правильно, тогда и отлаживать не придётся.
> (кто-нибудь видел debugger?
Много раз.
Но не пользуюсь.
Отладка в лиспах проводится очень просто.
И без употребления отладчика.
---
...Я работаю антинаучным аферистом...
> Так что язык программирования, подходящий под мою философию,
> пока вообще не изобрели.
Пролог изобрели тоже где-то в начале 70-х.
---
...Я работаю антинаучным аферистом...
Вообще-то, то, что SML'и и прочие OCaml'ы требуют заранее объявить все типы, это скорее особенность, чем необходимость. Вполне можно было бы добавить динамическое добавление новых функций и типов из программы (как в .NET) ценой потери производительности.
А можно сделать так, чтобы один и тот же код мог производить сериализацию/десериализацию
данных любых (ну пусть хоть только алгебраических) типов?
То есть функции типа read : string -> 'a и print : 'a -> string ?
с отрицанием возможности апгрейда по частям, как и положено в монотеизме и в *BSDНет такого отрицания. Если ты воин - пожалуйста, апгрейди по частям. Я так иногда делаю.
Это потому что идея строгой статической типизации не доведена до завершения,
так что я преувеличил, конечно.
Остаются операции приведения типов во время выполнения:
- запуск процессов, как правило, осуществляется из скрипта не шелле - нетипизированном языке
- программы преобразуют параметры командной строки и конфиги в свои внутренние структуры,
и на этом этапе может проявиться несовпадение типов (которые в этом случае называются форматами)
- всякие другие штуки, типа загрузки библиотек - тоже типы практически не проверяют
Идеально статически типизированная система включала бы в себя шелл с проверкой типов,
запрещающий передавать программам аргументы, которые они не поймут.
Так же нужно какое-то средство, чтобы нельзя было написать конфиг в неверном формате.
Сборка такой системы завершалась бы глобальной проверкой типов, и апгрейд был бы возможен только
полный, с потерей всех данных.
Сборка такой системы завершалась бы глобальной проверкой типов, и апгрейд был бы возможен только полный
Как это вообще связано? В SML есть раздельная компиляция...
Пролог изобрели тоже где-то в начале 70-х.
Да Пролог тоже не типизированный, я не про то. Я говорю, что само понятие типизации предполагает, что во главу всего ставится понятие свойства. Потому, что "Переменная x имеет тип X" <=> "То, что хранится в переменной x обладает свойством X". А отношения между переменными в современных системах типов не выразимы.
map :: (a->b) -> [a] -> [b]
Какие свойства объектов (и каких объектов) участвуют
в данном объявлении и в реализации такой конструкции?
То есть функции типа read : string -> 'a и print : 'a -> string ?
Hugs98/Lib/Exts/Haskell2Xml.hs ? Еще есть Hugs98/Lib/Exts/dynamic.lhs , на этом тоже вроде можно написать... Мне не хватило здоровья разбираться в этом Exts, там чего только нет!
Не очень-то она раздельная, как я помню.
Нужны ведь сигнатуры всех используемых модулей, и в них содержится
вся необходимая информация о типах.
Ты наверное не понимаешь, про что я говорю.
Вот пример.
Рассмотрим почти любой сетевой протокол - например HTTP.
В нём сразу были предусмотрены возможности расширения, благодаря чему
сейчас параллельно используются несколько версий - 1.0, 1.1,
совместимость с 0.9 сохраняется обычно, всякие промежуточные варианты
и нестандартные расширения.
Это возможно лишь благодаря тому, что такие протоколы в корне противоречат
идее строгой статической типизации - ведь соответствие протоколу проверяется в динамике.
Но ведь можно было определить протокол с помощью алгебраических типов и сигнатур,
тогда динамические проверки не были бы необходимы, если все участники коммуникации
прошли статическую проверку типов.
Всякие поля неизвестных заранее форматов, зарезервированные для будущих расширений,
не могли бы существовать, так как тип данных, передаваемых в этих полях,
не был бы известен во время компиляции.
Протокол определялся бы некоторой сигнатурой, описывающей формат всех сообщений.
При модификации протокола было бы необходимо перекомпилировать все программы,
его использующие, и если две программы были бы скомпилированы относительно разных сигнатур,
они не смогли бы взаимодействовать -- требовалась бы одновременная перекомпиляция и замена
всех программ.
Естественно, такой подход не масштабируется на достаточно сложные системы --
представь себе одновременный апгрейд всех web-серверов и всех браузеров в Интернет --
поэтому возможны только лишь отдельные "островки" со статической типизацией,
и динамическая проверка типов на их границах.
В случае Интернет эти границы есть модули, реализующие стандартные протоколы.
В случае ОС - модули, работающие с форматами файлов, параметрами командной строки,
и с теми же протоколами.
То есть спор, какая типизация круче, статическая или динамическая, не имеет смысла,
так как есть потребность и в том, и в другом.
map :: (a->b) -> [a] -> [ b]
Ну, это намного ближе, к тому, что я хочу, чем вышеописанный Clone. Наверное ко мне придет счастье вместе с Generics.NET Кстати ими заведует некто Andrew Kennedy, фанат функционального программирования, создатель SML.NET
PS Блин, а как заэкранировать [ b]?
То есть спор, какая типизация круче, статическая или динамическая, не имеет смысла,
так как есть потребность и в том, и в другом.
Пять баллов! Полностью согласен. Ну дык в Lisp-е же одной из них как раз и нет!..
Я с этим не разобрался ещё, но как я понял, подход такой:
можно по сути сделать расширение языка с любыми свойствами,
и компилятор для этих расширений с помощью макросов.
В том числе и статическую типизацию можно сделать таким образом,
и вроде как и сделано даже.
Причём проверка типов может быть и во время выполнения, например
при загрузке плагина, и всё это без магии, штатными средствами.
...производить сериализацию/десериализацию...
Во!
Тебя с Kennedy одни и те же вопросы интересуют Хотя мне не понравилось, потому, что у него получилось в итоге не так как ты хотел...
Выдайте кто-нибудь список свойств атома proclaim.
Или их уже отменили?
---
...Я работаю антинаучным аферистом...
Что неверно.
По меньшей мере, существуют числа, строки и атомы.
> А отношения между переменными в современных системах
> типов не выразимы.
Переменные --- это имена.
Какие могут быть соотношения между именами?
В большинстве языков имена недоступны изнутри.
---
...Я работаю антинаучным аферистом...
> - запуск процессов, как правило, осуществляется из скрипта не шелле
Замени sh на SML, что от этого изменится?
> - программы преобразуют параметры командной строки
> и конфиги в свои внутренние структуры,
Кто мешает проверить это при вводе команды?
> - всякие другие штуки, типа загрузки библиотек - тоже типы практически не проверяют
Что мешает им проверить?
> Так же нужно какое-то средство, чтобы нельзя было написать
> конфиг в неверном формате.
Мысли Чака Мура живут и процветают.
http://www.colorforth.com/cf.html
> Сборка такой системы завершалась бы глобальной проверкой типов,
> и апгрейд был бы возможен только полный, с потерей всех данных.
Связывание может быть отложенным.
---
...Я работаю антинаучным аферистом...
Как это можно сделать неявно?
---
...Я работаю антинаучным аферистом...
Переменные --- это имена.
А когда у переменной есть тип, то это нормально? Как у имени может быть тип? И вообще, имя чего? Переменной? Переменная - это имя переменной, а дальше рекурсия - переменная это имя имени переменной и т.д. Вобщем, как я уже сказал
На твои посты даже ответить нечегоодин только флуд
можно по сути сделать расширение языка с любыми свойствами,
и компилятор для этих расширений с помощью макросов.
В том числе и статическую типизацию можно сделать таким образом
Что-то в этом есть! Учитывая, что в Lisp-е, вообще, грань между компиляцией и исполнением крайне размытая. Как я понял, там на стадии компиляции можно сделать сколь угодно сложные действия, не то что позорный C препроцессор...
camlp4
и что-то подобное в хаскеле
а в лиспе то, про что я читал - это именно на стадии выполнения
но это не страшно - так как представление кода и данных одинаково, то всё,
что можно сделать во время выполнения, можно и во время компиляции, если
входные аргументы (программа на расширенном языке) уже известны
а в лиспе то, про что я читал - это именно на стадии выполнения
Я то в Lisp-е вообще ни бумбум разве что Схему из-за КОНТРы поставил, чтобы (apply...(map ... запускать Так что я все плету по воспоминаниям с курса ФП (с) Роганов (знаешь его наверное? )
PS Чего отвечаешь так быстро? Спать пора!
в данном контесте схема - не то
там нет макросов
(define-syntax fn
(syntax-rules (lambda) fn . l) (lambda . l
...
(map (fn (x) (* x x lst
---
...Я работаю антинаучным аферистом...
Нет.
Тип есть только у значения переменной.
> Как у имени может быть тип?
Только когда именем обозначаются значения единственного рода.
> И вообще, имя чего?
<<Заглавие этой песни называется "Пуговки для сюртуков.">>
---
...Я работаю антинаучным аферистом...
Тип есть только у значения переменной.
Ты всего лишь споришь с общепринятым способом употребления термина. Что, может пойти в gоogle и найти тебе миллион словосочетаний "тип переменной"? Только тебе одному приходит в голову, что человек, использующий это словосочетание, на самом деле не понимает его смысла... Тебя это обстоятельство не смущает?
Про "тип переменной" могу сказать просто: мир, в основном, жил и живёт с ранним связыванием.
---
...Я работаю антинаучным аферистом...
Если речь идет о ФЯ, то вообще нет смысла спорить о том, есть у переменных тип или нет. В ФЯ нет переменных как таковых и этот факт не сразу доходит до императивщиков. Переменные - это имена, алиасы для упрощения программирования, их невозможно оторвать от значений, на которые они ссылаются, поэтому у них не может быть никакого типа.
Значения появляются лишь во время выполнения, а проверка типов проходит раньше -
следовательно, типы проверяются не у значений.
А именно, у "переменных", т.е. типы присваиваются именам, определённым в каком-то контексте.
В математике так же: в записи $f(x) = x^2$ x называется переменной, хотя разве он меняется?
И все-таки типа у переменной нет, есть тип у выражения, на которое она ссылается, или константы. Не вижу причин приписывать дополнительные свойства обычной ссылке на другой объект, который на самом деле обладает этими свойствами.
Согласен, так точнее.
И тип 'переменной' и значения вообще может быть разный
Это факт! Тип переменной общее, чем тип объекта, на который она ссылается.
Не вижу причин приписывать дополнительные свойства обычной ссылке на другой объект
Ты хочешь сказать, что в Java не стоит указывать типы переменных только потому, что в ней есть RTTI?
И все-таки типа у переменной нет, есть тип у выражения, на которое она ссылаетсяВ ООП это заведомо не так, еще раз повторяю, тип переменной всегда более общий чем тип значения переменной, но эти типы вполне могут и не совпадать. В ФП в некотором смысле возможна противоположная ситуация, программист может наcильно сузить тип переменной
intMap :: (Int->Int) ->[Int] -> [Int]
intMap = map
Тип переменной intMap -- (Int->Int) ->[Int] -> [Int], а тип ее значения -- (a->b)->[a]->[ b].
PS Пажаалуйста! Не отправляя меня в FAQ научите написать [ b ] без пробелов
Чувак, я говорил о функциональных языках (чистых а не Явах и т.п. И типа у переменных в ФЯ нет, потому что нет переменных, поскольку по своей сути они просто ссылки на константы или выражения.
однако верно и для *ML тоже
если не называть переменной ссылки ('a ref)
В ФП в некотором смысле возможна противоположная ситуация, программист может наcильно сузить тип переменной
code:--------------------------------------------------------------------------------
intMap :: (Int->Int) ->[Int] -> [Int]
intMap = map
--------------------------------------------------------------------------------
Тип переменной intMap -- (Int->Int) ->[Int] -> [Int], а тип ее значения -- (a->b)->[a]->[ b].
Чего неправильно то сказал?
А теперь покажи мне программный продукт платный или бесплатный на этих самых языках.
FFTW (The Fastest Fourier Transform in the West). Код на C естественно (может даже Fortran? но он был не написан руками, а сгенерирован программой, написаной на OCaml. Вот.
ФЯ плохо ложаться на архитектуру современных компьютеров.
А императивные языки плохо ложатся на архитектуру будущих компьютеров. Все уже заманались Fortran распараллеливать!.. Приходится всякие извращения изобретать, OpenMP тот же, векторные операции, встроенные редукции...
Еще один серьезный минус, сложный код на ФЯ нечитаем, что является серьезным препятствием при исправлениях, поисках ошибки.
Зачастую программа на ФЯ состоит из трех строк, над которыми надо минутку подумать, а аналогичная программа на С занимает три экрана. Каждую строчку последней понимаешь сразу при прочтении, но разве это значит, что она проще? К тому же понять каждую строчку программы не означает понять саму программу, нужно понять их все.
gsl-1.4/fit/linear.c
GNU SL можно взять, например, из БСД.
Я уменьшил размер более, чем в полтора раза, переведя всё то же на Схему.
---
...Я работаю...
круто !
---
...Я работаю...
Так что всё откладывается.
Кому любопытно, смотрят в контрафактах.
---
...Я работаю антинаучным аферистом...
Потом для скорости перепишу на массивы,
а сейчас пусть будут списки.
---
...Я работаю...
Оставить комментарий
Dasar
Для изучения Лиспа нужно иметь довольно продвинутые мозги.ps
Как ты думаешь, почему программистов на Cobol-е и VB-е больше всего?
И даже pascal-истов больше, чем лисповцев, не говоря уже о явистах и c-ишниках.
pps
На функциональных языках, вообще, большие программы можно написать? Воообще, пишут?
кроме банального примера emacs-а.