Explaining functional programming to object-oriented programmers and less technical people

后端 未结 12 1430
小蘑菇
小蘑菇 2021-01-30 23:09

What are some good examples that I can use to explain functional programming?

The audience would be people with little programming experience, or people who only have ob

相关标签:
12条回答
  • 2021-01-30 23:48

    Compare it to sentence structure.

    I hit the ball, catch the ball, throw the ball:
    Procedural
    Hit(ball)
    Catch(ball)
    Throw(ball)

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

    0 讨论(0)
  • 2021-01-30 23:52

    Why Functional Programming Matters by John Hughes has some good examples, but even better, it explains the why of functional programming languages, not just the how. You could do much worse than build a presentation based on that paper!

    0 讨论(0)
  • 2021-01-30 23:54

    New answer. This will work for both non-programming power users and OO programmers.

    Point out that Excel is functional.

    Each cell in a spreadsheet is a variable that contains a function which declares a relationship between other cells/variables.

    0 讨论(0)
  • 2021-01-30 23:55

    Functional Programming for the Object-Oriented Programmer by by Brian Marick. I'm not associated, or anything, just came by it right now:

    https://leanpub.com/fp-oo

    Uncle Bob likes it! :)

    0 讨论(0)
  • 2021-01-30 23:56

    Immutable data structures

    You know, I've commented to people about functional programming before and have brought up the whole "immutable data structure" thing. This usually blows people's mind -- how are you supposed to add something to a collection if you can't actually add it?

    The immediate answer is "well you don't, you just create a new version of your data structure with the object added", and the immediate response is "that doesn't sound very efficient". Well it is.

    Immutable stack

    Canonical immutable data structure is an immutable stack, which is basically a node containing two readonly values: a head and a tail. Pushing and popping from the stack are O(1). Indexed lookups, inserts/deletes in the middle of the list require O(n) time, which incidentally is the same as a mutable stack.

    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);
            }
        }
    }
    

    Immutable AVL Tree

    Most people aren't impressed by stacks because it isn't a "serious" data structure, but AVL trees and other self-balancing data structures are pretty impressive. Trees are actually very easy to write as immutable data structures.

    This version supports O(log n) inserts and lookups, delete isn't shown but it can be implemented in O(log n) as well. In other words, the immutable AVL tree has the same computational complexity as as its mutable variant:

    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);
            }
        }
    }
    
    0 讨论(0)
  • 2021-01-30 23:57

    I don't think you can, frankly. Functional programming (like 'good' OO programming, vs. 'procedural with classes') requires a mental model shift. You just can't understand it as a procedural coder without actually taking the time and working through using it.

    If you really want someone to understand functional programming, get them a copy of the Little Schemer or some other equivalent book, and have them sit down and work through the exercises. Nothing else will help them build the necessary mental model.

    0 讨论(0)
提交回复
热议问题