Подтвердить что ты не робот

Может кто-то уточнить, что это означает цитата из Joel On Software: (функциональные программы не имеют побочных эффектов)

Я читал Joel On Software сегодня и наткнулся на эту цитату:

Без понимания функциональности программирования, вы не можете изобретать MapReduce, алгоритм, который делает Google настолько масштабируема. термины "Карта и сокращение" Lispи функциональное программирование. Уменьшение карты в ретроспективе очевидна для любого кто помнит их 6,001-эквивалентный класс программирования, который чисто функциональные программы имеют никаких побочных эффектов и, таким образом, тривиально параллелизуемое.

Что он имеет в виду, когда говорит, что функциональные программы не имеют побочных эффектов? И как это делает распараллеливание тривиальным?

4b9b3361

Ответ 1

Позвольте мне прочитать его для вас

Вкратце, чистая функция - это функция, которая вычисляет вещи, основанные только на ее заданных аргументах и ​​возвращает результат.

Запись чего-либо на экран или изменение глобальной переменной (или элемента данных) является побочным эффектом. Опираясь на данные, отличные от данных, приведенных в аргументе, также делает вашу функцию нечистой, хотя это не побочный эффект.

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

Ответ 2

Что он имеет в виду, когда говорит функциональные программы не имеют сторон эффекты?

Большинство людей думают о программировании как создании переменных, присваивании им значений, добавлении вещей в списки и т.д. Переменные "меняются", отсюда и название.

Функциональное программирование - это стиль разработки программ для устранения переменных - все является постоянным или только для чтения.

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

"Но Джульетта! Как написать полезную программу, если она ничего не может изменить"

Хороший вопрос!

Вы "изменяете" вещи, создавая новый экземпляр вашего объекта с измененным состоянием. Например:

class Customer
{
    public string Id { get; private set; }
    public string Name { get; private set; }

    public Customer(string id, string name)
    {
        this.Id = id;
        this.Name = name;
    }

    public Customer SetName(string name)
    {
        // returns a new customer with the given name
        return new Customer(this.Id, name);
    }
}

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

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

"Но Джульетта!" Как это может быть эффективно при этом копировании? "

Трюк понимает, что вам не нужно копировать весь граф объекта, а только те части, которые изменились. Если части вашего графического объекта не изменились, вы можете повторно использовать его в своем новом объекте (скопируйте указатель, не создавайте новый экземпляр каких-либо объектов в этой части графика).

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

В большинстве случаев неизменяемая версия структуры данных имеет одинаковую вычислительную сложность для вставки/поиска/удаления в качестве ее изменяемых копий. Единственное различие заключается в том, что вставка возвращает новую версию вашей структуры данных без изменения исходного.

И как это делает распараллеливание тривиальным?

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

Поскольку неизменяемые объекты по сути являются потокобезопасными, говорят, что они concurrency "тривиальны". Но это всего лишь половина истории. Бывают случаи, когда нам нужно, чтобы изменения в одном потоке были видимыми для другого - так как мы это делаем с неизменяемыми объектами?

Трюк состоит в том, чтобы переосмыслить нашу модель concurrency. Вместо того, чтобы два потока делились друг с другом, мы думаем о потоках как о виде почтового ящика, который может отправлять и получать сообщения.

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

Поверьте, стратегия выше делает параллельное программирование 1000x проще, чем блокировки в изменяемом состоянии. Поэтому важная часть комментария Джоэла: "Не понимая функционального программирования, вы не можете изобретать MapReduce, алгоритм, который делает Google настолько масштабируемым".

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

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

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

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

Смотрите также: http://www.defmacro.org/ramblings/fp.html

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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

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