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

Я читал Joel На программном обеспечении сегодня и натыкался на эту кавычку:

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

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

21
задан Gordon Gustafson 15 March 2010 в 21:04
поделиться

5 ответов

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

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

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

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

13
ответ дан 29 November 2019 в 20:12
поделиться

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

4
ответ дан 29 November 2019 в 20:12
поделиться

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

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

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

4
ответ дан 29 November 2019 в 20:12
поделиться

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

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

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

7
ответ дан 29 November 2019 в 20:12
поделиться

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

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

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

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

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

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

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

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, красно-черные деревья, многие виды куч и т. Д. См. здесь для получения дополнительной информации. реализация неизменяемого трепа.

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

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

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

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

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

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

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

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

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

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

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

См. Также: http://www.defmacro.org/ramblings/fp.html

19
ответ дан 29 November 2019 в 20:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: