Здравствуйте, prVovik, Вы писали:
INT>>Кстати, гибридный OCaml как раз тем и хорош, что можно императивную реализацию обернуть в чисто функциональный интерфейс не выходя, за пределы языка. V>Хрчу заметить, что построить функциональный интерфейс к императивной резализации можно, если передавать в качестве параметра всю необходимую для выполнения алгоритма информацию, причем передавать эту информанию необходимо не по ссылке, а по значению. Отсюда следует, что применять такой подход можно только в простейших случаях, когда объем передаваемой информации достаточно мал, чтобы можо было позволить передавать копию этой информации.
Убс, сначала написал, а потом сам понял, что не прав. Конечно, можно передавать и по ссылке. Не бейте сильно...
Здравствуйте, prVovik, Вы писали:
VQ>>Ты что-то путаешь. Зачем что-то регенерировать, есть вполне строгие методы позволяющие заменить один элемент в Q>>массиве на другой без копирования всего массива. V>Это как раз то, о чем я говорю. Если мы изменим элемент массива без его регенерации, то получим функцию с побочным эффектом. Массив теперь станет изменяемой сущностью, т.е. объектом, инкипсулирующим свое сотояние. Манипулировать такими изменяемыми сущностями, применяя чистый функциональный подход в общем случае невозможно, и хотим мы того, или нет, придется прсать итеративную программу. То есть на определенном уровне абстракции функциональный подход изчезает.
, где мы мерялись с С++. Там массив меняется с функциональной семантикой, но деструктивно. При этом Clean не допускает побочных эффектов. Парадокс? Нет, потому что массив single threaded (можешь по этим словам пустить google), т. е. на него не остается ссылок после "изменения", а до "изменения" была только одна. Здесь компилятор может применить (и применяет) деструктивное изменение, что и можно видеть посмотрев результат тестов.
Ну а про Haskell и How To Declare Imperative (Wadler) тебе если еще и не написали, то напишут.
, где мы мерялись с С++. Там массив меняется с функциональной семантикой, но деструктивно.
Для демонстрации сферического коня в вакуумме пример вполне сгодится...
G>При этом Clean не допускает побочных эффектов. Парадокс? Нет, потому что массив single threaded (можешь по этим словам пустить google), т. е. на него не остается ссылок после "изменения", а до "изменения" была только одна. Здесь компилятор может применить (и применяет) деструктивное изменение, что и можно видеть посмотрев результат тестов.
Я и не сомневался в том, что простейшие случаи можно оптимизировать. Но в реальной жизни (в отличии от коней в вакууме), на изменяемый объект очень часто существуют ссылки
G>Ну а про Haskell и How To Declare Imperative (Wadler) тебе если еще и не написали, то напишут.
Все, теперь ночь спать не буду...
Здравствуйте, prVovik, Вы писали:
V>Здравствуйте, Gaperton, Вы писали:
G>>Здравствуйте, prVovik, Вы писали:
G>>Гхм... Посмотри решето Эратосфена на Clean
, где мы мерялись с С++. Там массив меняется с функциональной семантикой, но деструктивно. V>Для демонстрации сферического коня в вакуумме пример вполне сгодится...
Я уже три раза писал что меряют скорость работы памяти, но не слышат. Вообще в этом тесте если выбирать те ограничения которые здесь были (~3e6 элементов) то bcc5.4 c отключенной оптимизацией показывает примерно одинаковую скорость с VC7.1 c /O2
>bcc32 -Od Eratosthenes.cpp
Borland C++ 5.4 for Win32 Copyright (c) 1993, 1999 Inprise Corporation
Eratosthenes.cpp:
Turbo Incremental Link 4.00 Copyright (c) 1997, 1999 Inprise Corporation
>Eratosthenes.exe
Count: 216816
Time: 1321
>cl /O2 Eratosthenes.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13.10.3077 for 80x86
Copyright (C) Microsoft Corporation 1984-2002. All rights reserved.
Eratosthenes.cpp
Microsoft (R) Incremental Linker Version 7.10.3077
Copyright (C) Microsoft Corporation. All rights reserved.
/out:Eratosthenes.exe
Eratosthenes.obj
>Eratosthenes.exe
Count: 216816
Time: 1281
>
дравствуйте, prVovik, Вы писали:
Q>>Ты что-то путаешь. Зачем что-то регенерировать, есть вполне строгие методы позволяющие заменить один элемент в Q>>массиве на другой без копирования всего массива. V>Это как раз то, о чем я говорю. Если мы изменим элемент массива без его регенерации, то получим функцию с побочным эффектом. Массив теперь станет изменяемой сущностью, т.е. объектом, инкипсулирующим свое сотояние. Манипулировать такими изменяемыми сущностями, применяя чистый функциональный подход в общем случае невозможно, и хотим мы того, или нет, придется прсать итеративную программу. То есть на определенном уровне абстракции функциональный подход изчезает.
Из локального изменения массива не следует возникновение строннего эффекта. Для того, чтобы этого добиться в чистых функциональных языках применяют разные методы, про Clean тебе ответил Gaperton, а в Haskell'e это решается с помощью монад — они позволяют упорядочить вычисления, добиться того, чтобы перед вычислением Б обязательно вычислялось А. Это продемонстрировано в моей версии решета, но если ты не знаешь языка, вряд ли ее поймешь. Если ты считаешь, что такие ограничения сильно стесняют и нормальную программу не напишешь, твое дело. Однако на Haskell'e написана большая часть его компилятора, а это немаленькая программа и она работает.
Дело в том, что реально такие сугубо императивные куски типа работы с массивами встречаются достаточно редко, их можно изолировать и обрабатывать отдельно в монадном-императивном стиле. Потом массив можно банально перевести в read-only форму и работать с ссылкой на него, не заморачиваясь проблемой копирования его целиком. Ты просто привык к императивщине и тебе кажется, что она везде и без нее и пару чисел не сложить. На самом деле ее мало, просто надо привыкнуть думать по другому.
Здравствуйте, Quintanar, Вы писали:
Q>Задача сугубо императивная, поэтому и стиль такой. Блин, нашли что обсуждать, ей богу, еще бы перемножение матриц сравнивали. Если на то пошло, нужно смотреть более функциональные задачи. У меня, например, есть пара парсеров на OCaml. Один интерпретор примитивного языка, а другой конвертатор из bnf в sml yacc. Могу их выложить, хотя я ими не очень доволен, писал их когда еще слабо разбирался в функциональном стиле.
Здравствуйте, prVovik, Вы писали:
V>Здравствуйте, INTP_mihoshi, Вы писали:
INT>>Любой изменяющийся объект можно представить в виде значения функции двуэ переменных — времени и "указателя" на объект. Значение этой функции от аргументов ("Возраст Иванова", "сейчас") и ("Возраст Иванова", "год назад") имеет различные аргументы, чледовательно, имеет различные значения. V>Во-первых, проблема не решена. Побочные эффекты будут проявляться при изменении времени.
Какие побочные эффекты?
V>Во-вторых, это простой случай. Что, если бы пришлось учитывать не возраст, а, например, количество волос на голове? Пришлось бы каждый момент времени сохранять текущее значение?
Это уже вопросы реализации.
V>Совершенно верно. Но проблему это всеравно не решает.
Какую проблему?
Любую задачу, любой алгоритм, который можно выразить с помощью ыормальной логики, можно описать в чисто функциональном стиле. Это доказывается. Если ты хочешь доказать обратное — не трудись, это невозможно. Если хочешь понять, как это делается, почитай про монады. Хотя бы на этом же форуме. Если тебе не понятны какие-то детали — задавай конкретный вопрос, я отвечу. Если тебе не понятно как решается в функциональном стиле какая-то конкретная задача — давай пример, покажу.
Здравствуйте, VladD2, Вы писали:
VD>Но C# — это строго-типизированный язык. Об этом даже в спецификации наисано. Так что ты ошибашся. INT>>Когда я ими пользовался, мне они показались неудобными и неинтуитивными. Может, с непривычки.
VD>Именно что. И вообще твое отвращение к Шарпу явно основано на том, что ты в него не вник.
Зачем же сразу отвращение. Бытовые задачи на нем писать приятнее, чем на С++ или яве. И вообще чем бы то ни было. Просто я не считаю его подходящим для cutting edge вещей
Здравствуйте, prVovik, Вы писали:
V>Объясню на пальцах... V>Вооружимся функциональным подходом и рассмотрим некоторую сущность, которая существует в пределах своей среды окружения. Например, среда окружения — это предприятие П1, а сущность — сотрудник Иванов, которому 35 лет. В один прекрасный день, этот сотрудник справляет себе день рождения, в результате чего Иванов(35) умирает, и на его месте появляется Иванов(36). Посмотрим на предприятие: раньше было предприятие П1, в котором работал сотрудник Иванов(35), теперь Иванов(35) умер, следовательно должно также умереть и предприятие, вместе с остальными сотрудниками. На месте этих трупов появится новое предприятие П2, в котором теперь работает сотрудник Иванов(36). Предприятие П1 также существовало в пределах своей среды окружения, например, множества других предприятий, с которыми оно взаимодействовало. И этим предприятиям также придется помереть со всеми своими сотрудниками после того, как на предприятии П1 сотрудник Иванов отпразнует свой день рождения.
Абсолютно верно. С философской точки зрения, ФП исповедует принцип "нельзя дважды войти в одну и ту же реку". Поскольку математики работают с абстрактными объектами произвольного масштаба, их никак не смущает ежесекундная смерть и возрождение вселенной.
Отличие между ФП и ИП — только в том, что в ИП четко фиксируется набор возможных изменений состояния. Предположим, что нашей вселенной является бесконечный одномерный массив целых чисел. С точки зрения ФП нам никто не запрещает ввести функцию F, которая отображает этот массив в развернутый на 180. Что-то типа F: (Universe' <- Universe[-i]). И эта функция ничуть не хуже такой, которая меняет местами ровно два элемента этого массива. В любом случае Universe умирает, и возникает Universe'.
С точки зрения ИП, мы располагаем очень ограниченным набором функций смены состояния вселенной. Наша вселенная от рождения умеет менять два элемента местами. Но любую "большую" функцию мы обязаны представить как суперпозицию этих более маленьких. Вот это представление и является императивной программой.
К сожалению, большинство макрофункций допускают [бесконечно] большое количество вариантов такого разложения. Именно за это нам платят деньги.
В итоге, императивное программирование предоставляет программисту возможность формулировать эти представления вручную. Функциональное программирование больше сосредотачивается на результате, чем на процессе. Это фактически попытка передать больше обязанностей по выполнению [i]той же задачи компилятору.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Lloyd, Вы писали:
L>>Удивительно, но у Кнута тогда он тоже перевран.
VD>На память говорить не хорошо, но вроде бы был верным. Уж точно сортировка была по месту. К тмоу же у алгоритма есть автор. И кнут к нему не имеет ни малейшего отношения. Так что классический, есть классический.
"По месту" — это деталь реализации. К алгоритму сортировки как таковому имеет отдаленное отношение. Если завтра изобретут новую, более эффективную реализацию — что, окажется, что Хоар тоже все переврал?
Еще такое соображение. Когда вы думаете об алгоритме быстрой сортировки, что вы вспоминаете — его математическое описание (а оно очень близко к программе на Haskell'е) или реализацию на C?
А вообще, конечно, на идеальном декларативном языке (синтаксис придумал сам, на ходу) алгоритм сортировки записывается примерно так:
sort (in Sequence seq, out Sequence sorted)
PRE{
// Предусловие - вероятно, пустое
}
POST{
// Постусловие - описание отсортированности последовательности sorted
}
А уж компилятор должен по этому описанию изобрести алгоритм быстрой сортировки, а заодно и его эффективную реализацию. Вот жизнь-то начнется, когда напишут такой компилятор
Здравствуйте, Banch, Вы писали:
Q>>Имея эту функцию мы можем создать новые функции типа Q>>
Q>>inc x = add 1 x
Q>>add10 x = add 10 x
Q>>
Q>>первая из которых увеличивает свой аргумент на 1, а вторая на 10. Разница с обычным вызовом более Q>>общей подфункции с некоторыми фиксированными аргументами заключается в том, что функция add 1 будет Q>>частично вычислена один раз и при дальнейших вызовах перевычислятся не будет.
B>не понял что там можно частично вычислить в выражении add 1 ?? заранее вычислить что 1 это 1 ?! B>даже допустим там стоит не 1, а sqrt(2), ну и что — я могу положить это один раз в статическую переменную
Может, не совсем ответ, но просто вспомнилось. Когда я еще учился в институте, мы часто писали алгоритмы оптимизации функций (градиентный метод и т.п.) Всегда стояла проблема — скажем, оптимизируется функция f(a1, a2, x1, x2, x3) по аргументам x1, x2, x3 с какими-то значениями параметров a1, a2. Были мы студентами и делали все по-студенчески — забивали конкретные значения a1, a2 в код (и перекомпилировали при каждом изменении), или передавали через глобальные переменные. Сейчас, глядя на математические библиотеки, написанные на C, я вижу, что в них принято передавать в оптимизируемую (или интегрируемую, дифференцируемую и т.п.) функцию дополнительный параметр void *data, в котором содержится голый указатель на пользовательские параметры (соотвественно, возникает куча приведений типов).
В студенческие годы я изобрел велосипед в виде объектно-ориентированного подхода к решению этой задачи — создавал объект с виртуальной функцией f(x1, x2, x3), который и передавал в функцию оптимизации (параметры a1, a2 лежали в объекте и задавались конструктором). Сейчас я бы, наверное, воспользовался каррингом.
Теперь, в свете этой истории, ответ. В выражении add 1 вычисляется функция. Она не имеет имени, а действие ее заключается в прибавлении 1 к аргументу. Это простейший пример. f a1 a2 — это функция от трех аргументов x1, x2, x3, которую можно смело передавать в другую функцию трехмерной оптимизации.
Если говорить чуть точнее, выражения add 1 и f a1 a2 создают так называемые замыкания (closures) — функции + ассоциированные с ними данные. Это понятие довольно базовое, но относится скорее к реализации. А вообще, сходство замыканий с объектами в ООП позволяет формализовать ООП в рамках функционального программирования, и даже реализовать объектные возможности.
Здравствуйте, gloomy rocker, Вы писали:
GR>Посмотри на F# GR>На нем можно писать код, используя FCL, в которой собрано огромное количество велосипедов.
Вот написал бы кто-нибудь вводную статью по F# и с примерами применения.... было бы реально полезно.
Здравствуйте, Alexey Chen, Вы писали:
AC>Здравствуйте, gloomy rocker, Вы писали:
GR>>Посмотри на F# GR>>На нем можно писать код, используя FCL, в которой собрано огромное количество велосипедов.
AC>Вот написал бы кто-нибудь вводную статью по F# и с примерами применения.... было бы реально полезно.
Честно говоря у меня дальше беглого взгляда на примеры, идущие в комплекте с F#, дело не пошло.
Так что вводную статью я тоже не против почитать.
Предлагаю добавить пару слов о значении типизации.
Сама по себе типизация не имеет отношения к функциональности того или иного языка. Но так уж сложилось, что когда говорят о типизированных функциональных языках, речь обычно идет о так называемой системе типов Хиндли-Милнера, использующейся в Haskell и Ocaml (или SML).
Система типов Хиндли-Милнера — конструкция, ортогональная свойству функциональности языка. Бывают функциональные языки, не использующие эту систему (правда, императивные языки с этой системой типов мне неизвестны). Давать формальное определение этой системы мне сейчас не хочется, да это и не нужно. Следует обратить внимание прежде всего на то, как правильное использование типов повышает надежность программ. (В этой фразе ключевое слово — "правильное".)
Начнем с простого примера (буду приводить примеры на Haskell). В какую-либо функцию нам нужно передать день недели и в зависимости от него что-нибудь в ней сделать. Самое простое решение — закодировать дни недели числами от 1 до 7, и писать что-нибудь вроде:
case day_of_week of
1 -> Делаем что-то для понедельника
2 -> Для вторника
...
7 -> Ура! Воскресенье!
К сожалению, в этом коде есть проблема: что произойдет, если day_of_week по какой-либо причине станет равным другому числу, отличному от 1...7? Другой недостаток — "магические числа" в коде. В С мы бы ввели enum и писали бы:
Читаемость повысилась. Однако в С нет особых трудностей создать ситуацию, когда day_of_week принимает значения, отличные от диапазона enum'а. Необходимо делать проверку (вводить default) и засорять код.
В Haskell'e (или любом другом языке с той же системой типов) мы вводим новый тип, аналогичный C-шному enum'у следующей конструкцией:
data Weekday = Monday | Tuesday | ... |Sunday
Пояснения: Многоточие — не элемент синтаксиса, а следствие лени
Новый тип называется Weekday, data — ключевое слово, означающее, что мы вводим новый тип
Вертикальная черта читается как "или" и разделяет варианты значений, которые может принимать переменная нового типа
Таким образом читается запись следующим образом: "введем новый тип Weekday, переменные которого могут принимать значения Monday или Tuesday и т.д."
Заметим, что здесь ничего не говорится о том, что такое Monday, Tuesday и т.д. — это неделимые конструкции, называемые "конструкторами данных". Никакое приведение типов к ним от целых или от строк невозможно, и если установлено, что переменная day_of_week имеет тип Weekday, можно спать спокойно: никаких других значений, отличных от указанных, она не примет не при каких обстоятельствах. В соответствующем коде:
case day_of_week of
Monday -> Делаем что-то для понедельника
Tuesday -> Для вторника
...
Sunday -> Ура! Воскресенье!
никаких дополнительных проверок делать не надо. Более того, если мы пропустим какой-либо день недели, компилятор выдаст нам предупреждение и даже укажет, какой именно день мы пропустили!
Конструкторы типов и сам тип могут иметь в качестве параметра другой тип — нечто вроде шаблонов С++. Скажем, рассмотрим следующий (стандартный) тип:
data Maybe a = Nothing | Just a
Что мы здесь видим? Определяется новый тип Maybe, параметризованный тИповой переменной a, в качестве которой можно использовать любой другой тип (конструкция аналогична template<class A> class Maybe). (Заметим в скобках, что в Haskell'e конкретные типы и типовые переменные различаются тем, что имя типа может начинаться только с заглавной буквы.) У нашего типа два конструктора — один (Nothing) без параметров, другой (Just) — с параметром. Таким образом, например, переменные типа Maybe Int могут принимать значения Nothing, Just 1, Just 2 и т. п.
Зачем это нужно? Вспомним старые добрые времена, язык С и функцию fopen. В случае успешного открытия файла она возвращает указатель на некоторую структуру, в случае ошибки — NULL. Огромное количество других функций так или иначе используют какие-либо выделенные значения возвращаемого типа функции для сигнализации об ошибках в них, причем делают это совершенно вразнобой: кто использует 0, кто 1, кто -1. Зачастую в возвращаемом типе не остается места для "ошибочных" значений, и приходится идти на ухищрения вроде глобальной переменной errno и т.п. И, что хуже всего, никто, кроме совести программиста, не может проконтролировать, что обработка ошибочных значений присутствует в программе.
В языке с системой типов Хиндли-Милнера функция fopen возвращала бы тип Maybe File, где File — тип, описывающий правильно открытый файл, а значение Nothing сигнализировало бы, что файл не открылся. Код, обрабатывающий эту ситуацию, выглядит примерно так:
case fopen(file_name, flags) of
Just file -> do_something(file)
Nothing -> Обрабатываем ошибочную ситуацию.
Здесь мы использовали сопоставление с образцом, что может служить темой отдельного разговора, но в данном случае должно быть понятно и так. Если возвращаемое значение функции fopen имеет вид Just file, где file — переменная типа File, соответствующая корректно открытому файлу, то мы вызываем функцию do_something. Если же файл не открыт, обрабатываем эту ситуацию.
Заметим, что функция do_somtheing принимает параметры типа File, т.е. предполагает, что в нее передаются только правильно открытые файлы. Мы не можем проигнорировать этот факт и написать do_something(fopen(file_name, flags)) — этот код не скомпилируется, поскольку не пройдет проверку по типам. Если мы забудем написать вариант для Nothing, компилятор выдаст нам предупреждение. Таким образом, компилятор помогает нам контролировать проверку возвращаемых значений функций!
PS. Несколько замечаний:
Пример с fopen можно улучшить. Так, вместо констатации простого факта, что файл не открылся, можно добавить информацию о том, по каким причинам это произошло. Для этого надо изменить возвращаемый тип функции fopen. Предоставляем это читателю в качестве упражнения (подсказка: тип может иметь несколько параметров)
В самом начале упоминалось важность правильного использования типов. Само по себе наличие системы Хиндли-Милнера в языке не делает программы на нем более надежными, грамотность в ее использовании очень важна. Так, мы могли и не вводить тип Weekday, а для возвращаемых значений функций по-старинке использовать -1 или что-нибудь в этом роде. Тогда все преимущества теряются.
Доказано, что программы на языке с системой типов Хиндли-Милнера не могут "упасть" с ошибками, связанными с доступом по неверному адресу, нулевому указателю.
Из сказанного еще не следует, что динамическая типизация не имеет права на жизнь или что программы на языке с динамической типизацием менее надежны.
Здравствуйте, faulx, Вы писали:
F>"По месту" — это деталь реализации. К алгоритму сортировки как таковому имеет отдаленное отношение. Если завтра изобретут новую, более эффективную реализацию — что, окажется, что Хоар тоже все переврал?
В приведенном виде сортировка уже может трактоваться как сортировка слиянием или даже пиповая. Хор изобрел конкретный алгоритм.
Переврать Хор ничего не мог так как он был изобретателем данного алгоритма.
Кстати, с филосовфской точки зрения реализация быстрой сортировки функциональными средствами невозможно так как это не алгоритмический подход. Вещь суть ФЯ отках от конкретного алгоритма в пользу декларативности. На ФЯ мы скорее выражаем класс алгоритма (логарифмический) нежели его самого. Я конечно понимаю, что это больше похоже на шутку, но это как раз та шутка в которой доля шутки не очень велика.
F>Еще такое соображение. Когда вы думаете об алгоритме быстрой сортировки, что вы вспоминаете — его математическое описание (а оно очень близко к программе на Haskell'е) или реализацию на C?
Алгоритм не может быть описан математически. Матемотически можно только задать принцип. А принцип скорее оностистя к лассу алгоритма. Ведь основной принцип в бустрой сортировке это рекурсивное деление списков на подсписки. Но под это определение начинают попадать и некоторые другие алгоритмы.
Ну, и главное, в рамках современных компьютеров быстрой эта сортировка может быть только если она не ведет к расходу памяти.
К тому, же как я говорил у Хора за "средний" брался то ли элемент массива находящийся посередине, то ли послебний (правый). Если брать не средний элемент, то на сортированном списке алгоритм будет существнно медленнее.
F>А вообще, конечно, на идеальном декларативном языке (синтаксис придумал сам, на ходу) алгоритм сортировки записывается примерно так:
F>
F>sort (in Sequence seq, out Sequence sorted)
F>PRE{
F> // Предусловие - вероятно, пустое
F>}
F>POST{
F> // Постусловие - описание отсортированности последовательности sorted
F>}
F>
F>А уж компилятор должен по этому описанию изобрести алгоритм быстрой сортировки, а заодно и его эффективную реализацию.
Это, как я понимаю, называется логическим программированием.
F>Вот жизнь-то начнется, когда напишут такой компилятор
Прологу уже много лет. Кстати, не факт, что такие простые вещи как сортировка не будет проще описать алгоритмически. Ну, а глваная проблема тут в том, что компилятор должен знать конкретные алгоритмы и уметь подбирать наиболее подходящий в конкретном случае, а то и уметь изобретать алгоритмы и выбирать наиболее подходящий. Ну, а это уже искуственный интелект. Да даже не искуственный, а идеальный. Ведь далеко не каждый человек знает все нужные алгоритмы и умеет правильно их выбирать. Но мечат о зеленой факсовй кномке нажатие на ктороую автоматически решает луюбую сложную проблему — это та мечта к которой нужно стремиться всему человечесту.
... << RSDN@Home 1.1.4 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, INTP_mihoshi, Вы писали:
INT>Зачем же сразу отвращение. Бытовые задачи на нем писать приятнее, чем на С++ или яве. И вообще чем бы то ни было. Просто я не считаю его подходящим для cutting edge вещей
Ну, это смотря чего резать.
... << RSDN@Home 1.1.4 beta 2 >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, faulx, Вы писали:
F>Зачем это нужно? Вспомним старые добрые времена, язык С и функцию fopen. В случае успешного открытия файла она возвращает указатель на некоторую структуру, в случае ошибки — NULL. Огромное количество других функций так или иначе используют какие-либо выделенные значения возвращаемого типа функции для сигнализации об ошибках в них, причем делают это совершенно вразнобой: кто использует 0, кто 1, кто -1. Зачастую в возвращаемом типе не остается места для "ошибочных" значений, и приходится идти на ухищрения вроде глобальной переменной errno и т.п. И, что хуже всего, никто, кроме совести программиста, не может проконтролировать, что обработка ошибочных значений присутствует в программе.
Тут для полноты картины надо еще и про исключения вспомнить.
... << RSDN@Home 1.1.4 rev. 185 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, prVovik, Вы писали:
V>Здравствуйте, Gaperton, Вы писали:
G>>Здравствуйте, prVovik, Вы писали:
G>>Гхм... Посмотри решето Эратосфена на Clean
, где мы мерялись с С++. Там массив меняется с функциональной семантикой, но деструктивно. V>Для демонстрации сферического коня в вакуумме пример вполне сгодится...
Вообще, это была демонстрация элементарной работы с массивами. Ты уверен, что поймешь более сложный пример? А то если тебе конь в вакууме привиделся... Вобщем, не вижу смысла делать это в форуме, заинтересованный человек в состоянии найти более сложные примеры сам.
G>>При этом Clean не допускает побочных эффектов. Парадокс? Нет, потому что массив single threaded (можешь по этим словам пустить google), т. е. на него не остается ссылок после "изменения", а до "изменения" была только одна. Здесь компилятор может применить (и применяет) деструктивное изменение, что и можно видеть посмотрев результат тестов. V>Я и не сомневался в том, что простейшие случаи можно оптимизировать. Но в реальной жизни (в отличии от коней в вакууме), на изменяемый объект очень часто существуют ссылки
Ой! Правда?
G>>Ну а про Haskell и How To Declare Imperative (Wadler) тебе если еще и не написали, то напишут. V>Все, теперь ночь спать не буду...
Хочешь — не спи. Я на твою ночь не претендую
Здравствуйте, FR, Вы писали:
FR>Я уже три раза писал что меряют скорость работы памяти, но не слышат. Вообще в этом тесте если выбирать те ограничения которые здесь были (~3e6 элементов) то bcc5.4 c отключенной оптимизацией показывает примерно одинаковую скорость с VC7.1 c /O2
Ну если ты настаиваешь... Напиши решето эратосфена на Python (без psyco), и померяй им скорость работы памяти. Боюсь, что память почему-то будет работать медленнее , наверно программа на питоне ее сломает.
Мы вообще-то компиляторы сравнивали, на одном алгоритме. Точнее, эффективность работы с массивами в чистом ФЯ сравнительно с С++. А не устанавливали рекорды скорости. Поэтому и не слышим.
То, что С++ и Clean уперлись в память, говорит об эффективной работе с массивами. Haskell не смог "упереться в память". Боюсь у питона тоже не получится.