什么任务是在功能的编程风格最好的呢? [英] What task is best done in a functional programming style?

查看:171
本文介绍了什么任务是在功能的编程风格最好的呢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近刚发现的函数式编程风格,我相信,这将减少开发力度,使code更容易阅读,使软件更易于维护。然而,问题是我在说服任何人都吸了进去。

好了,最近我有机会就如何降低软件开发和维护工作的演讲,我想向他们介绍函数式编程,以及如何有利于团队的概念。我有这个想法向人们展示2套code,做同样的事情,在一个非常迫切的方法之一codeD,和其他在一个非常实用的方式,展现了函数式编程可以做code路短,更容易理解,因此维护。有这样一个例子,由Luca博洛尼亚广场例如著名的总和旁?


解决方案

  

我刚刚最近发现函数式编程风格[...]
  好了,最近我有机会就如何减少通话
  软件开发工作,我
  希望引进的概念
  函数式编程。


如果你刚刚发现函数式编程,我的的建议试图在这个问题权威说话。我知道前6个月,而我主义学习F#,我所有的code的只是C#有一点比较尴尬的语法。然而,一段时间后,我能写出一贯良好的code在地道,实用的风格。

我建议你做同样的:等待6个月左右,直到函数式编程风格来更自然,然后给你presentation


  

我试着
  说明功能的好处
  编程,和我的想法
  向人们展示2套code,做的
  在一个非常同一件事,一是codeD
  势在必行方式,另在一个
  非常实用的方法,以表明
  函数式编程可以做code
  路短,更容易理解和
  从而维护。是否有这样的例子,
  广场的著名总和旁
  例如,通过卢卡·波隆尼?


我给了一个F#presentation到.NET用户组在我区,很多人在我的组是由F#的模式匹配pssed IM $ P $。具体来说,我展示了如何遍历C#和F#抽象语法树:

 使用系统;命名空间的ConsoleApplication1
{
    公共接口IExprVisitor< T>
    {
        ŧ访问(TrueExpr表达式);
        ŧ访问(和表达式);
        ŧ访问(NAND表达式);
        ŧ访问(或表达式);
        ŧ访问(XOR表达式);
        ŧ访问(不表达式);    }    公共抽象类Expr的
    {
        公共抽象接受内< T>(IExprVisitor< T>游客);
    }    公共抽象类UnaryOp:Expr的
    {
        公共Expr的第一个{搞定;私人集; }
        公共UnaryOp(表达式第一)
        {
            this.First =第一;
        }
    }    公共抽象类BinExpr:Expr的
    {
        公共Expr的第一个{搞定;私人集; }
        公共Expr的二{搞定;私人集; }        公共BinExpr(表达式第一,EXPR秒)
        {
            this.First =第一;
            this.Second =秒;
        }
    }    公共类TrueExpr:Expr的
    {
        公共重写接受内< T>(IExprVisitor< T>访客)
        {
            返回visitor.Visit(本);
        }
    }    公共类:BinExpr
    {
        公众和(表达式第一,EXPR秒):基地(第一,第二){}
        公共重写接受内< T>(IExprVisitor< T>访客)
        {
            返回visitor.Visit(本);
        }
    }    公共类的Nand:BinExpr
    {
        公众的Nand(表达式第一,EXPR秒):基地(第一,第二){}
        公共重写接受内< T>(IExprVisitor< T>访客)
        {
            返回visitor.Visit(本);
        }
    }    公共类或者:BinExpr
    {
        公共或(表达式第一,EXPR秒):基地(第一,第二){}
        公共重写接受内< T>(IExprVisitor< T>访客)
        {
            返回visitor.Visit(本);
        }
    }    公共类异或:BinExpr
    {
        公共异或(表达式第一,EXPR秒):基地(第一,第二){}
        公共重写接受内< T>(IExprVisitor< T>访客)
        {
            返回visitor.Visit(本);
        }
    }    公共类不:UnaryOp
    {
        市民不要(表达式在前):基地(第一){}
        公共重写接受内< T>(IExprVisitor< T>访客)
        {
            返回visitor.Visit(本);
        }
    }    公共类EvalVisitor:IExprVisitor<布尔>
    {
        公共BOOL访问(TrueExpr表达式)
        {
            返回true;
        }        公共BOOL访问(和表达式)
        {
            返回的eval(expr.First)及和放大器;的eval(expr.Second);
        }        公共BOOL访问(NAND表达式)
        {
            返回(EVAL(expr.First)及和放大器;的eval(expr.Second))!;
        }        公共BOOL访问(或表达式)
        {
            返回的eval(expr.First)||的eval(expr.Second);
        }        公共BOOL访问(XOR表达式)
        {
            返回的eval(expr.First)^的eval(expr.Second);
        }        公共BOOL访问(不表达式)
        {
            返回的eval(expr.First)!;
        }        公共BOOL评估和演示(表达式表达式)
        {
            返回expr.Accept(本);
        }
    }    公共类prettyPrintVisitor:IExprVisitor<串GT;
    {
        公共字符串访问(TrueExpr表达式)
        {
            返回True;
        }        公共字符串访问(和表达式)
        {
            返回的String.Format(({0})和({1}),expr.First.Accept(本),expr.Second.Accept(本));
        }        公共字符串访问(NAND表达式)
        {
            返回的String.Format(({0})NAND({1}),expr.First.Accept(本),expr.Second.Accept(本));
        }        公共字符串访问(或表达式)
        {
            返回的String.Format(({0})OR({1}),expr.First.Accept(本),expr.Second.Accept(本));
        }        公共字符串访问(XOR表达式)
        {
            返回的String.Format(({0})XOR({1}),expr.First.Accept(本),expr.Second.Accept(本));
        }        公共字符串访问(不表达式)
        {
            返回的String.Format(不({0}),expr.First.Accept(本));
        }        公共字符串pretty(表达式表达式)
        {
            返回expr.Accept(本).Replace((真),真);
        }
    }    类节目
    {
        静态无效TestLogicalEquivalence(表达式第一,EXPR秒)
        {
            VAR prettyPrinter =新的prettyPrintVisitor();
            VAR的eval =新EvalVisitor();
            VAR evalFirst = eval.Eval(第一);
            VAR evalSecond = eval.Eval(第二);            Console.WriteLine(测试前pressions:);
            Console.WriteLine(首先= {0},prettyPrinter pretty(第一));
            Console.WriteLine(评估和演示(一):{0},evalFirst);
            Console.WriteLine(第二= {0},prettyPrinter pretty(二));
            Console.WriteLine(评估和演示(二):{0},evalSecond);;
            Console.WriteLine(?相当于{0},evalFirst == evalSecond);
            Console.WriteLine();
        }        静态无效的主要(字串[] args)
        {
            VAR P =新TrueExpr();
            变种Q =新的不(新TrueExpr());            TestLogicalEquivalence(P,Q);            TestLogicalEquivalence(
                新的不(P)
                新的NAND(P,P));            TestLogicalEquivalence(
                新和(P,Q),
                新的NAND(新的NAND(P,Q),新的NAND(P,Q)));            TestLogicalEquivalence(
                新的或(P,Q)
                新的NAND(新的NAND(P,P),新的NAND(Q,Q)));            TestLogicalEquivalence(
                新XOR(P,Q),
                新的NAND(
                    新的NAND(P,新的NAND(P,Q)),
                    新的NAND(Q,新的NAND(P,Q)))
                );            Console.ReadKey(真);
        }
    }
}

在code上面写的是一个习惯用法C#风格。它采用访问者模式,而不是类型的测试,以确保类型安全。这是关于218 LOC。

这里的F#版本:

  #light
开放系统键入EXPR =
    |真正
    |和expr的* EXPR
    | expr的NAND * EXPR
    |或expr的* EXPR
    | expr的异或* EXPR
    |不是EXPR让(^^)p Q =没有(P&安培;&放,Q)及和放大器; (P || Q)//临时XOR运算符让REC的eval函数=
    |真 - >真正
    |与(E1,E2) - >的eval(E1)及&放大器;的eval(E2)
    | NAND(E1,E2) - >不(EVAL(E1)及&放大器;的eval(E2))
    |或(E1,E2) - >的eval(E1)||的eval(E2)
    |异或(E1,E2) - >的eval(E1)^^的eval(E2)
    |不(E1) - >不(EVAL(E1))让REC prettyPrint E =
    让REC循环=功能
        |真 - > 真正
        |与(E1,E2) - > sprintf的(%S)和(%S)(环E1)(环E2)
        | NAND(E1,E2) - > sprintf的(%S)的NAND(%S)(环E1)(环E2)
        |或(E1,E2) - > sprintf的(%S)或(%S)(环E1)(环E2)
        |异或(E1,E2) - > sprintf的(%S)XOR(%S)(环E1)(环E2)
        |不(E1) - > sprintf的NOT(%S)(环E1)
    (循环E).Replace((真),真)让testLogicalEquivalence E1 E2 =
    让EVAL1,EVAL2 = EVAL E1,E2的eval
    printfn测试前pressions:
    printfn首先=%S(prettyPrint E1)
    printfn的eval(E1):%BEVAL1
    printfn第二=%S(prettyPrint E2)
    printfn的eval(E2):%BEVAL2
    printfnEquilalent?%B(EVAL1 = EVAL2)
    printfn让P,Q =真的,不是真
让测试=
    [
        P,Q;        不(P),NAND(P,P);        和(P,Q),
            NAND(与非(P,Q),NAND(P,Q));        或(P,Q),
            NAND(与非(P,P),NAND(Q,Q));        异或(P,Q),
            NAND(
                    NAND(对,NAND(P,Q)),
                    NAND(Q,NAND(P,Q))
                )
    ]
测试|> Seq.iter(乐趣(E1,E2) - > testLogicalEquivalence E1 E2)Console.WriteLine((preSS任意键))
Console.ReadKey(真)|>忽视

这是65 LOC。由于它采用模式匹配,而不是访问者模式,我们不会失去任何类型的安全,和code是非常容易阅读。

任何的一种象征性的处理是数量级更容易在F#中比C#写的。

哦,模式匹配不只是为访问者模式的替代品,它也可以让你匹配的的形状的数据。例如,下面是其中的Nand的转换为它们的等价功能:

 让REC简化=功能
    | NAND(P,Q)当P = Q - >没有(简化P)
    | NAND(与非(P1,Q1),NAND(P2,Q2))
        当等价[P1; P2]&功放;&安培;相当于[Q1; Q2]
                     - >和(P1简化,简化Q1)
    | NAND(与非(P1,P2),NAND(Q1,Q2))
        当等价[P1; P2]&功放;&安培;相当于[Q1; Q2]
                     - >或(简化P1,简化Q1)
    | NAND(与非(P1,NAND(P2,Q1)),NAND(Q2,NAND(P3,Q3)))
        当等价[P1; P2; P3&放大器;&安培;相当于[Q1; Q2; Q3]
                     - > XOR(P1简化,简化Q1)
    | NAND(P,Q) - > NAND(简化P,简化Q)
    |真 - >真正
    |和(P,Q) - >和(简化P,简化Q)
    |或(P,Q) - >或(简化P,简化Q)
    |异或(P,Q) - > XOR(简化P,简化Q)
    |不(非p) - >简化p
    |不(对) - >没有(简化P)

它不可能写这篇code简明都在C#。

I've just recently discovered the functional programming style and I'm convinced that it will reduce development efforts, make code easier to read, make software more maintainable. However, the problem is I sucked at convincing anyone.

Well, recently I was given a chance to give a talk on how to reduce software development and maintenance efforts, and I wanted to introduce them the concept of functional programming and how it benefit the team. I had this idea of showing people 2 set of code that does exactly the same thing, one coded in a very imperative way, and the other in a very functional way, to show that functional programming can made code way shorter, easier to understand and thus maintainable. Is there such an example, beside the famous sum of squares example by Luca Bolognese?

解决方案

I've just recently discovered the functional programming style [...] Well, recently I was given a chance to give a talk on how to reduce software development efforts, and I wanted to introduce the concept of functional programming.

If you've only just discovered functional programming, I do not recommend trying to speak authoritatively on the subject. I know for the first 6 months while I was learnig F#, all of my code was just C# with a little more awkward syntax. However, after that period of time, I was able to write consistently good code in an idiomatic, functional style.

I recommend that you do the same: wait for 6 months or so until functional programming style comes more naturally, then give your presentation.

I'm trying to illustrate the benefits of functional programming, and I had the idea of showing people 2 set of code that does the same thing, one coded in a very imperative way, and the other in a very functional way, to show that functional programming can made code way shorter, easier to understand and thus maintain. Is there such example, beside the famous sum of squares example by Luca Bolognese?

I gave an F# presentation to the .NET users group in my area, and many people in my group were impressed by F#'s pattern matching. Specifically, I showed how to traverse an abstract syntax tree in C# and F#:

using System;

namespace ConsoleApplication1
{
    public interface IExprVisitor<t>
    {
        t Visit(TrueExpr expr);
        t Visit(And expr);
        t Visit(Nand expr);
        t Visit(Or expr);
        t Visit(Xor expr);
        t Visit(Not expr);

    }

    public abstract class Expr
    {
        public abstract t Accept<t>(IExprVisitor<t> visitor);
    }

    public abstract class UnaryOp : Expr
    {
        public Expr First { get; private set; }
        public UnaryOp(Expr first)
        {
            this.First = first;
        }
    }

    public abstract class BinExpr : Expr
    {
        public Expr First { get; private set; }
        public Expr Second { get; private set; }

        public BinExpr(Expr first, Expr second)
        {
            this.First = first;
            this.Second = second;
        }
    }

    public class TrueExpr : Expr
    {
        public override t Accept<t>(IExprVisitor<t> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class And : BinExpr
    {
        public And(Expr first, Expr second) : base(first, second) { }
        public override t Accept<t>(IExprVisitor<t> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class Nand : BinExpr
    {
        public Nand(Expr first, Expr second) : base(first, second) { }
        public override t Accept<t>(IExprVisitor<t> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class Or : BinExpr
    {
        public Or(Expr first, Expr second) : base(first, second) { }
        public override t Accept<t>(IExprVisitor<t> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class Xor : BinExpr
    {
        public Xor(Expr first, Expr second) : base(first, second) { }
        public override t Accept<t>(IExprVisitor<t> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class Not : UnaryOp
    {
        public Not(Expr first) : base(first) { }
        public override t Accept<t>(IExprVisitor<t> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class EvalVisitor : IExprVisitor<bool>
    {
        public bool Visit(TrueExpr expr)
        {
            return true;
        }

        public bool Visit(And expr)
        {
            return Eval(expr.First) && Eval(expr.Second);
        }

        public bool Visit(Nand expr)
        {
            return !(Eval(expr.First) && Eval(expr.Second));
        }

        public bool Visit(Or expr)
        {
            return Eval(expr.First) || Eval(expr.Second);
        }

        public bool Visit(Xor expr)
        {
            return Eval(expr.First) ^ Eval(expr.Second);
        }

        public bool Visit(Not expr)
        {
            return !Eval(expr.First);
        }

        public bool Eval(Expr expr)
        {
            return expr.Accept(this);
        }
    }

    public class PrettyPrintVisitor : IExprVisitor<string>
    {
        public string Visit(TrueExpr expr)
        {
            return "True";
        }

        public string Visit(And expr)
        {
            return string.Format("({0}) AND ({1})", expr.First.Accept(this), expr.Second.Accept(this));
        }

        public string Visit(Nand expr)
        {
            return string.Format("({0}) NAND ({1})", expr.First.Accept(this), expr.Second.Accept(this));
        }

        public string Visit(Or expr)
        {
            return string.Format("({0}) OR ({1})", expr.First.Accept(this), expr.Second.Accept(this));
        }

        public string Visit(Xor expr)
        {
            return string.Format("({0}) XOR ({1})", expr.First.Accept(this), expr.Second.Accept(this));
        }

        public string Visit(Not expr)
        {
            return string.Format("Not ({0})", expr.First.Accept(this));
        }

        public string Pretty(Expr expr)
        {
            return expr.Accept(this).Replace("(True)", "True");
        }
    }

    class Program
    {
        static void TestLogicalEquivalence(Expr first, Expr second)
        {
            var prettyPrinter = new PrettyPrintVisitor();
            var eval = new EvalVisitor();
            var evalFirst = eval.Eval(first);
            var evalSecond = eval.Eval(second);

            Console.WriteLine("Testing expressions:");
            Console.WriteLine("    First  = {0}", prettyPrinter.Pretty(first));
            Console.WriteLine("        Eval(First):  {0}", evalFirst);
            Console.WriteLine("    Second = {0}", prettyPrinter.Pretty(second));
            Console.WriteLine("        Eval(Second): {0}", evalSecond);;
            Console.WriteLine("    Equivalent? {0}", evalFirst == evalSecond);
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            var P = new TrueExpr();
            var Q = new Not(new TrueExpr());

            TestLogicalEquivalence(P, Q);

            TestLogicalEquivalence(
                new Not(P),
                new Nand(P, P));

            TestLogicalEquivalence(
                new And(P, Q),
                new Nand(new Nand(P, Q), new Nand(P, Q)));

            TestLogicalEquivalence(
                new Or(P, Q),
                new Nand(new Nand(P, P), new Nand(Q, Q)));

            TestLogicalEquivalence(
                new Xor(P, Q),
                new Nand(
                    new Nand(P, new Nand(P, Q)),
                    new Nand(Q, new Nand(P, Q)))
                );

            Console.ReadKey(true);
        }
    }
}

The code above is written in an idiomatic C# style. It uses the visitor pattern rather than type-testing to guarantee type safety. This is about 218 LOC.

Here's the F# version:

#light
open System

type expr =
    | True
    | And of expr * expr
    | Nand of expr * expr
    | Or of expr * expr
    | Xor of expr * expr
    | Not of expr

let (^^) p q = not(p && q) && (p || q) // makeshift xor operator

let rec eval = function
    | True          -> true
    | And(e1, e2)   -> eval(e1) && eval(e2)
    | Nand(e1, e2)  -> not(eval(e1) && eval(e2))
    | Or(e1, e2)    -> eval(e1) || eval(e2)
    | Xor(e1, e2)   -> eval(e1) ^^ eval(e2)
    | Not(e1)       -> not(eval(e1))

let rec prettyPrint e =
    let rec loop = function
        | True          -> "True"
        | And(e1, e2)   -> sprintf "(%s) AND (%s)" (loop e1) (loop e2)
        | Nand(e1, e2)  -> sprintf "(%s) NAND (%s)" (loop e1) (loop e2)
        | Or(e1, e2)    -> sprintf "(%s) OR (%s)" (loop e1) (loop e2)
        | Xor(e1, e2)   -> sprintf "(%s) XOR (%s)" (loop e1) (loop e2)
        | Not(e1)       -> sprintf "NOT (%s)" (loop e1)
    (loop e).Replace("(True)", "True")

let testLogicalEquivalence e1 e2 =
    let eval1, eval2 = eval e1, eval e2
    printfn "Testing expressions:"
    printfn "    First  = %s" (prettyPrint e1)
    printfn "        eval(e1): %b" eval1
    printfn "    Second = %s" (prettyPrint e2)
    printfn "        eval(e2): %b" eval2
    printfn "    Equilalent? %b" (eval1 = eval2)
    printfn ""

let p, q = True, Not True
let tests =
    [
        p, q;

        Not(p), Nand(p, p);

        And(p, q),
            Nand(Nand(p, q), Nand(p, q));

        Or(p, q),
            Nand(Nand(p, p), Nand(q, q));

        Xor(p, q),
            Nand(
                    Nand(p, Nand(p, q)),
                    Nand(q, Nand(p, q))
                )
    ]
tests |> Seq.iter (fun (e1, e2) -> testLogicalEquivalence e1 e2)

Console.WriteLine("(press any key)")
Console.ReadKey(true) |> ignore

This is 65 LOC. Since it uses pattern matching rather than the visitor pattern, we don't lose any type-safety, and the code is very easy to read.

Any kind of symbolic processing is orders of magnitude easier to write in F# than C#.

[Edit to add:] Oh, and pattern matching isn't just a replacement for the visitor pattern, it also allows you to match against the shape of data. For example, here's a function which converts Nand's to their equivalents:

let rec simplify = function
    | Nand(p, q) when p = q -> Not(simplify p)
    | Nand(Nand(p1, q1), Nand(p2, q2))
        when equivalent [p1; p2] && equivalent [q1; q2]
                    -> And(simplify p1, simplify q1)
    | Nand(Nand(p1, p2), Nand(q1, q2))
        when equivalent [p1; p2] && equivalent [q1; q2]
                    -> Or(simplify p1, simplify q1)
    | Nand(Nand(p1, Nand(p2, q1)), Nand(q2, Nand(p3, q3)))
        when equivalent [p1; p2; p3] && equivalent [q1; q2; q3]
                    -> Xor(simplify p1, simplify q1)
    | Nand(p, q) -> Nand(simplify p, simplify q)
    | True          -> True
    | And(p, q)     -> And(simplify p, simplify q)
    | Or(p, q)      -> Or(simplify p, simplify q)
    | Xor(p, q)     -> Xor(simplify p, simplify q)
    | Not(Not p)    -> simplify p
    | Not(p)        -> Not(simplify p)

Its not possible to write this code concisely at all in C#.

这篇关于什么任务是在功能的编程风格最好的呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆