Объяснение функционального программирования объектно-ориентированным программистам и менее техническим специалистам

class String
  def integer?
    Integer(self)
    return true
  rescue ArgumentError
    return false
  end
end
  1. Не имеет префикса is_. Я нахожу это глупым в вопросительных методах, мне нравится "04".integer? намного лучше, чем "foo".is_integer?.
  2. Он использует разумное решение sepp2k, которое проходит для "01" и т. Д.
  3. Объектно-ориентированный, yay.
22
задан rsideb 19 February 2010 в 04:24
поделиться

10 ответов

При вызове java используйте флаг -Xmx Flag, например -Xmx512m для размера кучи 512 мег. Вы также можете рассмотреть флаг -xms, чтобы начать кучу больше, если вы собираетесь, чтобы он растет с самого начала. Размер по умолчанию - 128 мег.

-121--885937-

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

-121--2681459-

Аудитория будет люди с небольшой опыт программирования,

Действительно ли можно объяснить функциональное программирование вместе с OO или процедурным или любой парадигмой программирования людям без большого опыта программирования?

или людям, у которых есть только объектно-ориентированный опыт.

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

using System;

namespace Juliet
{
    interface IConvertor<T, U>
    {
        U Convert(T value);
    }

    class Program
    {
        static U[] Convert<T, U>(T[] input, IConvertor<T, U> convertor)
        {
            U[] res = new U[input.Length];
            for (int i = 0; i < input.Length; i++)
            {
                res[i] = convertor.Convert(input[i]);
            }
            return res;
        }

        class SquareInt : IConvertor<int, string>
        {
            public string Convert(int i)
            {
                return (i * i).ToString();
            }
        }

        class ScaleInt : IConvertor<int, string>
        {
            readonly int Scale;

            public ScaleInt(int scale)
            {
                this.Scale = scale;
            }

            public string Convert(int i)
            {
                return (i * Scale).ToString();
            }
        }

        static void Main(string[] args)
        {
            int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            string[] squared = Convert<int, string>(nums, new SquareInt());
            string[] tripled = Convert<int, string>(nums, new ScaleInt(3));
        }
    }
}

Его простой, читаемый, объектно-ориентированный, его даже родовой так он работает для любых произвольных типов, так что в чем проблема? Для начала, его раздутый: у меня есть определение интерфейса и две реализации интерфейса. Что если мне понадобится еще одно преобразование? Мне нужна другая реализация интерфейса - она может быстро выйти из-под контроля.

Когда вы думаете об этом, класс IConvertor < T, U > является просто оберткой вокруг одной функции, называемой Convert - класс буквально существует, чтобы помочь нам передать Convert в другие функции. Если вы можете это понять, то вы уже понимаете основные принципы, лежащие в основе функций как первоклассных значений - функциональное программирование заключается в передаче функций другим функциям почти так же, как вы передаете человека или int или строку.

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

using System;

namespace Juliet
{
    class Program
    {
        static U[] Convert<T, U>(T[] input, Func<T, U> convertor)
        {
            U[] res = new U[input.Length];
            for (int i = 0; i < input.Length; i++)
            {
                res[i] = convertor(input[i]);
            }
            return res;
        }

        static string SquareInt(int i)
        {
            return (i * i).ToString();
        }

        static void Main(string[] args)
        {
            int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            string[] squared = Convert<int, string>(nums, SquareInt); // pass function by name
            string[] tripled = Convert<int, string>(nums, i => (i * 3).ToString()); // or pass anonymously
        }
    }
}

Хорошо, так что теперь у нас есть точно такая же программа в гораздо меньшем количестве строк кода, его читаемых, и его очень очевидно, что он делает.

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

class FileFunctions
{
    internal void SaveFile()
    {
        SaveFileDialog fileDialog = new SaveFileDialog();
        fileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
        if (fileDialog.ShowDialog() == DialogResult.OK)
        {
            File.AppendAllText(fileDialog.FileName, MyDocument.Data);
        }
    }

    internal void WriteFile()
    {
        OpenFileDialog fileDialog = new OpenFileDialog();
        fileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
        if (fileDialog.ShowDialog() == DialogResult.OK)
        {
            MyDocument.Data = File.ReadAllText(fileDialog.FileName);
        }
    }
}

Yuck. Код повторяется, но он выполняет операции над различными классами и вызывает различные методы. Как бы вы абстрагировали дублированный код во вселенной ОО? В функциональном программировании это тривиально:

class FileFunctions
{
    internal void ShowDialogThen(FileDialog dialog, Action<FileDialog> f)
    {
        dialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            f(dialog);
        }
    }

    internal void SaveFile()
    {
        ShowDialogThen(
            new SaveFileDialog(),
            dialog => File.AppendAllText(dialog.FileName, MyDocument.Data));
    }

    internal void WriteFile()
    {
        ShowDialogThen(
            new OpenFileDialog(),
            dialog => { MyDocument.Data = File.ReadAllText(dialog.FileName); });
    }
}

Внушительно.

31
ответ дан 29 November 2019 в 03:49
поделиться

Возможно, было бы проще сначала описать функциональное программирование по его фундаментальным характеристикам:

  1. Функции (очевидно)
  2. Нет побочных эффектов
  3. Неизменяемость
  4. Рекурсия очень важна
  5. Высокая степень параллелизации

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

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

9
ответ дан 29 November 2019 в 03:49
поделиться

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

Для сторонников ООП можно показать, что закрытие подобно инкапсуляции, и, таким образом, функциональное программирование охватывает ООП. Можно также показать, насколько полезны функции высшего порядка, в частности, как они заставляют некоторые паттерны ООП (такие как паттерны Strategy и Template, а также паттерн Visitor в языках с мультиметодами) растворяться в языке (Питер Норвиг утверждает, что 16 из 23 паттернов GOF проще в Dylan и Lisp). В качестве аналога, ООП на основе передачи сообщений (а не подход CLOS) является паттерном в FP, который невидим в ООП.

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

Вы можете или не хотите показывать людям ОО мультиметоды CLOS; они либо взбунтуются, либо впадут в гикгазм. В любом случае, это, скорее всего, будет грязно.

Некоторые другие возможности, показывающие гибкость FP: ленивая оценка может быть реализована с помощью нулевых анонимных функций. FP может поддерживать ООП как на основе классов, так и на основе прототипов. Есть множество других примеров (например, стиль continuation-passing), но они обычно требуют знакомства с FP перед изучением. В

SICP есть много интересных примеров и проблем; он должен оказаться вдохновляющим.

5
ответ дан 29 November 2019 в 03:49
поделиться
git checkout sha1_here  -- foo.m

или без проверки файла и просмотра его из командной строки

git show sha1_here:path_to_file/foo.m
-121--4859352-

http://www.regular-expressions.info/floatingpoint.html

-121--2435802-

Попробуйте просмотреть статью Функциональное программирование для остальной части U ниже.

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

3
ответ дан 29 November 2019 в 03:49
поделиться

Не уверен, как взломать этот звук, но как можно добавить прозрачную границу?

-121--1029726-

Возможно, есть способ сделать это с помощью Qt - я немного обошел документы QKeySequence, прежде чем сдаться, но вы можете просто использовать сигнал . В данный момент у меня нет настройки Qt/C + + на машине, но у меня есть привязки Python.

import sys, signal
from PyQt4 import QtGui

app = QtGui.QApplication(sys.argv)
signal.signal(signal.SIGINT, signal.SIG_DFL)

sys.exit(app.exec_())

Это работает и закроет приложение, когда я сделаю Ctrl-C . Так что я считаю, что ваше приложение может адаптировать этот код, и в конечном итоге это будет примерно так:

#include <signal.h>

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    ... // parse command line options

    if (no_gui) {
        signal(SIGINT, SIG_DFL);
    }

    ... 

    return app.exec();
}

К сожалению, я не могу это скомпилировать, так что ему, вероятно, потребуется несколько исправлений, но это должно дать вам общее представление. С помощью обработчика SIG _ DFL программа должна использовать действие по умолчанию, связанное с Ctrl-C .

-121--1615340-

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

3
ответ дан 29 November 2019 в 03:49
поделиться

ОО - это существительные , в то время как функциональное программирование - это глаголы . Прочтите этот довольно хороший блог на эту тему Стива Йегге .

3
ответ дан 29 November 2019 в 03:49
поделиться

Честно говоря, я не думаю, что вы сможете. Функциональное программирование (например, «хорошее» объектно-ориентированное программирование в сравнении с «процедурным с классами») требует изменения ментальной модели. Вы просто не можете понять это как процедурный кодер, не потратив время и не проработав его.

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

1
ответ дан 29 November 2019 в 03:49
поделиться

Неизменяемые структуры данных

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

Непосредственный ответ: «ну, не надо, вы просто создаете новую версию своей структуры данных. с добавленным объектом ", и немедленный ответ" звучит не очень эффективно ". Ну это так.

Неизменяемый стек

Каноническая неизменяемая структура данных - это неизменяемый стек, который по сути представляет собой узел, содержащий два значения, доступные только для чтения: начало и конец. Выталкивание и выталкивание из стопки - O (1). Индексированный поиск, вставка / удаление в середине списка требует времени O (n), что, кстати, совпадает с изменяемым стеком.

using System;

namespace Juliet
{
    public abstract class Stack<T>
    {
        public static readonly Stack<T> Empty = new Nil();

        public abstract T Head { get; }
        public abstract Stack<T> Tail { get; }
        public abstract bool IsEmpty { get; }
        public Stack<T> Push(T value) { return new Cons(value, this); }
        public Stack<T> Append(Stack<T> other)
        {
            if (this.IsEmpty) { return other; }
            else { return new Cons(this.Head, this.Tail.Append(other)); }
        }

        sealed class Nil : Stack<T>
        {
            public override T Head { get { throw new Exception("Empty stack"); } }
            public override Stack<T> Tail { get { throw new Exception("Empty stack"); } }
            public override bool IsEmpty { get { return true; } }
        }

        sealed class Cons : Stack<T>
        {
            private readonly T _head;
            private readonly Stack<T> _tail;

            public override T Head { get { return _head; ; } }
            public override Stack<T> Tail { get { return _tail; } }
            public override bool IsEmpty { get { return false; } }

            public Cons(T head, Stack<T> tail)
            {
                _head = head;
                _tail = tail;
            }
        }
    }

    class Program
    {       
        static void Main(string[] args)
        {
            var s = Stack<int>.Empty.Push(1).Push(2).Push(3).Push(4).Push(5);
            while (!s.IsEmpty)
            {
                Console.Write("{0} ", s.Head);
                s = s.Tail;
            }
            Console.ReadKey(true);
        }
    }
}

Неизменяемое дерево AVL

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

Эта версия поддерживает вставку и поиск O (log n), удаление не отображается, но также может быть реализовано в O (log n). Другими словами, неизменное дерево AVL имеет ту же вычислительную сложность, что и его изменяемый вариант:

using System;
using System.Linq;

namespace Juliet
{
    public abstract class AvlTree<T>
        where T : IComparable<T>
    {
        static AvlTree<T> Make(AvlTree<T> left, T value, AvlTree<T> right)
        {
            return new Node(left, value, right, Math.Max(left.Height, right.Height) + 1, left.Count + right.Count + 1);
        }

        public static readonly AvlTree<T> Empty = new Nil();

        protected abstract AvlTree<T> Left { get; }
        protected abstract AvlTree<T> Right { get; }
        protected abstract T Value { get; }
        public abstract int Height { get; }
        public abstract int Count { get; }

        public bool Contains(T v)
        {
            if (this.Height == 0) { return false; }
            else
            {
                int compare = v.CompareTo(this.Value);
                if (compare < 0) { return this.Left.Contains(v); }
                else if (compare > 0) { return this.Right.Contains(v); }
                else { return true; }
            }
        }

        public AvlTree<T> Insert(T v)
        {
            if (this.Height == 0) { return Make(this, v, this); }
            else
            {
                int compare = v.CompareTo(this.Value);
                if (compare < 0) { return Balance(Make(this.Left.Insert(v), this.Value, this.Right)); }
                else if (compare > 0) { return Balance(Make(this.Left, this.Value, this.Right.Insert(v))); }
                else { return this; }
            }
        }

        private static AvlTree<T> Balance(AvlTree<T> tree)
        {
            if (tree.Height > 0)
            {
                int outerBalanceFactor = tree.Left.Height - tree.Right.Height;
                if (outerBalanceFactor <= -2 && tree.Right.Height > 0) // right-side too heavy
                {
                    int innerBalanceFactor = tree.Right.Left.Height - tree.Right.Right.Height;
                    if (innerBalanceFactor <= -1) // right-right case
                    {
                        return LeftRotate(tree);
                    }
                    else if (innerBalanceFactor >= 1) // right-left case
                    {
                        return DoubleLeftRotate(tree);
                    }
                }
                else if (outerBalanceFactor >= 2 && tree.Left.Height > 0) // left-side too heavy
                {
                    int innerBalanceFactor = tree.Left.Left.Height - tree.Left.Right.Height;
                    if (innerBalanceFactor <= -1) // left-left case
                    {
                        return DoubleRightRotate(tree);
                    }
                    else if (innerBalanceFactor >= 1) // left-right case
                    {
                        return RightRotate(tree);
                    }
                }
            }
            return tree;
        }

        private static AvlTree<T> LeftRotate(AvlTree<T> tree)
        {
            if (tree.Height > 0 && tree.Right.Height > 0)
            {
                T p = tree.Value;
                T q = tree.Right.Value;
                AvlTree<T> a = tree.Left;
                AvlTree<T> b = tree.Right.Left;
                AvlTree<T> c = tree.Right.Right;
                return Make(Make(a, p, b), q, c);
            }
            return tree;
        }

        private static AvlTree<T> RightRotate(AvlTree<T> tree)
        {
            if (tree.Height > 0 && tree.Left.Height > 0)
            {
                T q = tree.Value;
                T p = tree.Left.Value;
                AvlTree<T> a = tree.Left.Left;
                AvlTree<T> b = tree.Left.Right;
                AvlTree<T> c = tree.Right;
                return Make(a, p, Make(b, q, c));
            }
            return tree;
        }

        private static AvlTree<T> DoubleLeftRotate(AvlTree<T> tree)
        {
            if (tree.Height > 0)
            {
                // right-rotate right child, left-rotate root
                return LeftRotate(Make(tree.Left, tree.Value, RightRotate(tree.Right)));
            }
            return tree;
        }

        private static AvlTree<T> DoubleRightRotate(AvlTree<T> tree)
        {
            if (tree.Height > 0)
            {
                // left-rotate left child, right-rotate root
                return RightRotate(Make(LeftRotate(tree.Left), tree.Value, tree.Right));
            }
            return tree;
        }


        sealed class Nil : AvlTree<T>
        {
            protected override AvlTree<T> Left { get { throw new Exception("Empty tree"); } }
            protected override AvlTree<T> Right { get { throw new Exception("Empty tree"); } }
            protected override T Value { get { throw new Exception("Empty tree"); } }
            public override int Height { get { return 0; } }
            public override int Count { get { return 0; } }
        }

        sealed class Node : AvlTree<T>
        {
            readonly AvlTree<T> _left;
            readonly AvlTree<T> _right;
            readonly T _value;
            readonly int _height;
            readonly int _count;

            protected override AvlTree<T> Left { get { return _left; } }
            protected override AvlTree<T> Right { get { return _right; } }
            protected override T Value { get { return _value; } }
            public override int Height { get { return _height; } }
            public override int Count { get { return _count; } }

            public Node(AvlTree<T> left, T value, AvlTree<T> right, int height, int count)
            {
                _left = left;
                _right = right;
                _value = value;
                _height = height;
                _count = count;
            }
        }
    }

    class Program
    {       
        static void Main(string[] args)
        {
            var tree = AvlTree<int>.Empty;
            foreach(int i in Enumerable.Range(1, 1000000).OrderBy(_ => Guid.NewGuid()))
            {
                tree = tree.Insert(i);
            }

            Console.Write("Tree height: {0}, tree count: {1}", tree.Height, tree.Count);

            Console.ReadKey(true);
        }
    }
}
2
ответ дан 29 November 2019 в 03:49
поделиться

Compare it to sentence structure.

Я бью по мячу, ловлю мяч, бросаю мяч:
Процедурный
Удар(мяч)
Поймать(мяч)
Throw(ball)

OO
Ball.Hit()
Ball.Catch()
Ball.Throw()

0
ответ дан 29 November 2019 в 03:49
поделиться

Мы использовали это руководство для Haskell, и оно сработало очень хорошо, беззаботно, но неплохо справляется с объяснениями:

Изучите Haskell для Great Good

1
ответ дан 29 November 2019 в 03:49
поделиться