在函数式编程风格中最好完成哪些任务?

时间:2020-12-24 20:03:32

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?

好吧,最近我有机会就如何减少软件开发和维护工作发表演讲,我想向他们介绍函数式编程的概念以及它如何使团队受益。我有这样的想法,即向人们展示两组完全相同的代码,一个以非常强制的方式编码,另一个以非常实用的方式编写,以表明函数式编程可以使代码更简单,更容易理解和因此可维护。有没有这样的例子,除了Luca Bolognese的着名的正方形例子之外?

16 个解决方案

#1


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.

如果你刚刚发现函数式编程,我不建议尝试就这个主题进行权威性的讨论。我知道在我学习F#的前6个月里,我的所有代码都只是C#,语法稍微笨拙。然而,在那段时间之后,我能够以惯用的功能风格编写一致的优秀代码。

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

我建议你这样做:等待6个月左右,直到函数式编程风格更自然地出现,然后给你的演示文稿。

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#:

我向我所在地区的.NET用户组发了一个F#演示文稿,我小组中的很多人都对F#的模式匹配印象深刻。具体来说,我展示了如何遍历C#和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.

上面的代码是用惯用的C#风格编写的。它使用访客模式而不是类型测试来保证类型安全。这大概是218 LOC。

Here's the F# version:

这是F#版本:

#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.

这是65 LOC。由于它使用模式匹配而不是访问者模式,因此我们不会丢失任何类型安全性,并且代码非常易于阅读。

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

任何类型的符号处理都比F#更容易用F#写入。

[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:

[编辑添加:]哦,模式匹配不仅仅是访问者模式的替代品,它还允许您匹配数据的形状。例如,这是一个将Nand转换为等价的函数:

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#.

不可能在C#中简明扼要地编写这段代码。

#2


There are plenty examples out there but none will be as impact full as using a sample relevant to one of your projects at work. Examples like "Sum Of Squares" by Luca are awesome but if someone used that as proof as to how our code base could be written better I would not be convinced. All the example proves is some things are better wrote functionally. What you need to prove is your code base is better written functionally

有很多例子,但没有一个像使用与你工作的一个项目相关的样本那样充满影响力。像Luca的“Sum Of Squares”这样的例子很棒但是如果有人用它作为我们的代码库如何写得更好的证据我就不会相信。所有的例子证明有些东西在功能上写得更好。您需要证明的是您的代码库在功能上更好地编写

My advice would be to pick some popular trouble spots and some core spots in the code base, and rewrite them in a functional style. If you can demonstrate a substantially better solution, it will go a long way to winning over co-workers.

我的建议是选择一些流行的麻烦点和代码库中的一些核心点,并以功能样式重写它们。如果您能够展示出更好的解决方案,那么赢得同事将会有很长的路要走。

#3


Tasks for functional style? Any time you have a common coding pattern and want to reduce it. A while ago I wrote a bit on using C# for functional style, while making sure it's practical: Practical Functional C# (I'm hesitate to link to my own stuff here, but I think it's relevant in this case). If you have a common "enterprise" application, showing, say, how expressions are lovely in pattern matching won't be too convincing.

功能风格的任务?任何时候你有一个共同的编码模式,并希望减少它。不久之前,我写了一些关于使用C#的功能样式,同时确保它是实用的:实用功能C#(我很犹豫,在这里链接到我自己的东西,但我认为这在这种情况下是相关的)。如果您有一个共同的“企业”应用程序,那么显示表达式在模式匹配中的可爱程度将不会太令人信服。

But in real-world apps, there are TONS of patterns that pop up at a low, coding level. Using higher order functions, you can make them go away. As I show in that set of blog posts, my favourite example is WCF's "try-close/finally-abort" pattern. The "try/finally-dispose" pattern is so common it got turned into a language keyword: using. Same thing for "lock". Those are both trivially represented as higher order functions, and only because C# didn't support them originally do we need hard-coded language keywords to support them. (Quick: switch your "lock" blocks out to use a ReaderWriter lock. Oops, we'll have to write a higher order function first.)

但在现实世界的应用程序中,有很多模式会以较低的编码级别出现。使用更高阶的函数,你可以让它们消失。正如我在那篇博文中所展示的那样,我最喜欢的例子是WCF的“try-close / finally-abort”模式。 “try / finally-dispose”模式非常常见,它变成了一个语言关键字:using。 “锁定”也是一样。这些都被简单地表示为高阶函数,并且仅仅因为C#最初不支持它们,我们需要硬编码语言关键字来支持它们。 (快速:切换你的“锁定”块以使用ReaderWriter锁。糟糕,我们必须先写一个更高阶的功能。)

But perhaps convincing just requires looking at Microsoft. Generics aka parametric polymorphism? That's hardly OO, but a nice functional concept that, now, everyone loves. The cute Ninject framework wouldn't work without it. Lambdas? As expression trees, they're how LINQ, Fluent NHibernate, etc. get all their power. Again, that doesn't come from OO or imperative programming. The new Threading library? Pretty ugly without closures.

但也许令人信服只需要看微软。泛型又是参数多态?这不是OO,而是一个很好的功能概念,现在,每个人都喜欢。如果没有它,可爱的Ninject框架将无法运行。 Lambda表达式?作为表达树,他们是如何LINQ,Fluent NHibernate等获得他们所有的力量。同样,这不是来自OO或命令式编程。新的线程库?非常丑陋没有封闭。

So, functional programming has been blessing things like .NET over the last decade. The major advances (such as generics, "LINQ") are directly from functional languages. Why not realise there's something to it and get more involved in it? That's how I'd phrase it to skeptics.

因此,在过去十年中,函数式编程一直在为.NET之类的事物带来祝福。主要的进步(例如泛型,“LINQ”)直接来自函数式语言。为什么不意识到它有什么东西并且更多地参与其中?这就是我对怀疑论者的看法。

The bigger problem is actually getting people to make the jump in understanding to higher order functions. While it's quite easy, if you've never seen it before in your life, it might be shocking an incomprehensible. (Heck, seems like a lot of people think generics are just for type-safe collections, and LINQ is just embedded SQL.)

更大的问题实际上是让人们在理解更高阶函数方面有所突破。虽然它很容易,如果你以前从未见过它,那可能会令人难以理解。 (哎呀,似乎很多人认为泛型只是用于类型安全的集合,而LINQ只是嵌入式SQL。)

So, what you should do is go through your codebase, and find places that are an overly-complicated imperative nightmare. Search for the underlying patterns, and use functions to string it together nicely. If you can't find any, you might settle for just demo'ing off lists. For example "find all the Foos in this list and remove them". That's a 1 line thing in functional style "myList.Remove(x=>x.Bla > 0)" versus 7 lines in C# style (create a temp list, loop through and add to-remove items, loop though and remove the items).

所以,你应该做的就是浏览你的代码库,找到一个过于复杂的必要噩梦。搜索底层模式,并使用函数将它们很好地串在一起。如果你找不到任何东西,你可能会满足于只是演示关闭列表。例如,“在此列表中找到所有Foos并将其删除”。功能样式“myList.Remove(x => x.Bla> 0)”中的1行与C#样式中的7行相比(创建临时列表,循环并添加删除项,循环并删除项)。

The hope is that, even though the syntax is odd, people will recognize "wow, that's a lot simpler". If they can put down the "verbose == more readable" and "that looks confusing" for a bit, you'll have a chance.

希望是,即使语法很奇怪,人们也会认识到“哇,这更简单”。如果他们可以放下“详细= =更具可读性”和“看起来令人困惑”,你就有机会。

Good luck.

#4


Essentially, the functional paradigm is highly effective for parallel processing:

从本质上讲,功能范例对并行处理非常有效:

"The really interesting thing I want you to notice, here, is that as soon as you think of map and reduce as functions that everybody can use, and they use them, you only have to get one supergenius to write the hard code to run map and reduce on a global massively parallel array of computers, and all the old code that used to work fine when you just ran a loop still works only it's a zillion times faster which means it can be used to tackle huge problems in an instant.

“我想让你注意到的真正有趣的事情是,只要你想到map并将其缩减为每个人都可以使用的函数,并且他们使用它们,你只需要一个超级因子来编写硬代码来运行在全局大规模并行计算机阵列上进行映射和缩减,以及在运行循环时过去工作正常的所有旧代码仍然只能运行数十亿次,这意味着它可以用于瞬间解决大问题。

Lemme repeat that. By abstracting away the very concept of looping, you can implement looping any way you want, including implementing it in a way that scales nicely with extra hardware."

勒米重复一遍。通过抽象出循环的概念,你可以以任何你想要的方式实现循环,包括以一种可以与额外硬件很好地扩展的方式实现循环。

http://www.joelonsoftware.com/items/2006/08/01.html

#5


The best advocacy paper ever written for the functional style is a paper by John Hughes called Why Functional Programming Matters. I suggest you do some examples for yourself until you reach the stage where you can convincingly make the arguments laid out in that paper.

为功能风格编写的最佳宣传论文是John Hughes撰写的一篇名为Why Functional Programming Matters的论文。我建议你自己做一些例子,直到你达到可以令人信服地在论文中提出论点的阶段。

Many of the examples in the paper are numerical and do not resonate with today's audiences. One more contemporary exercise I gave my students was to use the ideas in that paper to pack large media files onto 4.7GB DVDs for backup. They used Michael Mitzenmacher's "bubble search" algorithm to generate alternative packings, and using this algorithm and Hughes's techniques it was easy to get each DVD (except the last) 99.9% full. Very sweet.

本文中的许多例子都是数字的,不会与今天的观众产生共鸣。我给学生们的另一个当代练习是使用那篇论文中的想法将大型媒体文件打包到4.7GB DVD上进行备份。他们使用Michael Mitzenmacher的“泡泡搜索”算法来生成替代包装,并且使用这种算法和Hughes的技术,很容易让每张DVD(除了最后一张)满99.9%。很甜。

#6


Another example would be the Quicksort algorithm. It can be described very briefly in a functional language like Haskell:

另一个例子是Quicksort算法。它可以用像Haskell这样的函数式语言进行简要描述:

qsort []     = []
qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)

but needs some more coding in an iterative language. On the referenced Website you can also find a lot of other examples with comparisons between languages.

但需要在迭代语言中进行更多编码。在引用的网站上,您还可以找到许多其他语言比较的例子。

#7


In order to achieve what you want, and to communicate it to others in your organisation you need to demonstrate your company's business being built in a better way.

为了实现您的目标,并将其传达给组织中的其他人,您需要证明公司的业务是以更好的方式构建的。

Its no use using a couple of algorithms to demonstrate the power of functional programming if its totally useless for your business domain. So, take some existing code and rewrite it functionally. If you can prove through that, that it is better, people will listen to you - you've shown them a concrete, relevant example. IF you cannot, then perhaps functional programming is not the solution you've been looking for.

如果它对您的业务领域完全无用,那么使用几种算法来展示函数式编程的强大功能是没有用的。因此,请使用一些现有代码并在功能上重写它。如果你能证明这一点,那就更好了,人们会听你的 - 你已经向他们展示了一个具体的,相关的例子。如果你不能,那么函数式编程可能不是你一直在寻找的解决方案。

#8


If by the 'functional style' you mean the usage of concepts like 'map', 'apply', 'reduce', 'filter', lambda functions and list comprehensions, then it should be evident that code that has to deal with operations on lists is almost always more concise when written in the 'functional style'. But if you're mixing 'functional style' with imperative code in a mostly imperative language, it really is just a matter of style.

如果用'功能样式'表示使用'map','apply','reduce','filter',lambda函数和列表推导等概念,那么很明显那些必须处理操作的代码使用“功能样式”编写时,列表几乎总是更简洁。但是,如果你将“功能风格”与命令式代码混合在一种大多数命令式语言中,那真的只是风格问题。

In python for example, you can reimplement the Haskell qsort crackmigg posted like so:

例如,在python中,您可以重新实现Haskell qsort crackmigg,如下所示:

def qsort(list):
    if list == []:
        return []
    else:
        x = list[0]; xs = list[1:]
        return qsort(filter(lambda y: y<x, xs)) + [x] + qsort(filter(lambda y: y>= x, xs))

Although writing the last line as

虽然把最后一行写成了

return qsort([y for y in xs if y<x]) + [x] + qsort([y for y in xs if y>=x])

is arguably more 'pythonic'.

可以说更像是“pythonic”。

But this is obviously more concise than the implementation here:

但这显然比这里的实现简洁:

http://hetland.org/coding/python/quicksort.html

Which, incidentally, is how I would have thought about implementing it before I learned Haskell.

顺便提一下,在我学习Haskell之前,我是如何考虑实现它的。

The functional version is extremely clear if and only if you are acclimated to functional programming and grok what filter is going to do as easily as the well-worn C++ programmer groks a for loop without even having to think much about it. And that's clearly what the real issue at stake here: functional 'style' programming is a completely different mindset. If the people you work with aren't used to thinking recursively, and aren't the type to get excited about, not just a new technology, but a whole other way of thinking about solving their problems, then any amount of code comparisons isn't going to win them over.

功能版本非常清晰,当且仅当你适应了函数式编程并且很容易理解过滤器将要做的事情,就像老旧的C ++程序员在没有考虑它的情况下进行for循环一样容易。这显然是真正的问题所在:功能性“风格”编程是一种完全不同的心态。如果与你一起工作的人不习惯递归思考,并且不是那种兴奋的类型,不仅仅是一种新技术,而是另一种思考解决问题的方法,那么任何数量的代码比较都不是不会赢得他们。

#9


A good example cood be creating your own programming language using existing one, where you will have to use Monads.

一个很好的例子是使用现有的编程语言创建自己的编程语言,您必须使用Monads。

With F# it's much much simplier to write parsing logic than with C#.

使用F#,编写解析逻辑要比使用C#简单得多。

Take a look at this article: Functional .NET - LINQ or Language Integrated Monads?

看看这篇文章:功能.NET - LINQ或语言集成Monads?

#10


Algorithms involving backtracking search and simplifying undo support in GUIs are two places I've used functional style in practice.

涉及回溯搜索和简化GUI中撤消支持的算法是我在实践中使用功能样式的两个地方。

#11


A simple example of a task that is often easiest done in a functional style is the transformation of data from one form to another. "Sum of squares" is a trivial example of data transformation. Luca's talk at last year's PDC showed how to use this sort of data transformation for something more practical, downloading and analyzing stock quotes. The demo is done in F#, but the concepts are the same and can be applied to C# or most other programming languages.

通常在功能样式中最容易完成的任务的简单示例是将数据从一种形式转换为另一种形式。 “平方和”是数据转换的一个简单例子。 Luca在去年的PDC演讲中展示了如何使用这种数据转换来实现更实用,下载和分析股票报价。演示在F#中完成,但概念是相同的,可以应用于C#或大多数其他编程语言。

http://channel9.msdn.com/pdc2008/TL11/

#12


Show them jQuery's way of iterating over DOM elements:

向他们展示jQuery迭代DOM元素的方式:

$(".magic-divs").click(function(){
    // FYI, in this context, "this" will be the element clicked on.
    alert("somebody clicked on " + this.id);
    this.hide();

});

$(".magic-divs").show();

vs. how most google results for "javascript element by classname" do it:

与大多数谷歌搜索“javascript element by classname”的结果相比:

var arrayOfElements = // this is filled with the elements somehow
for(var i=0,j=arrayOfElements.length; i<j; i++) {
   alert("now I get to add an onclick event somehow " + i);
}
// i dont even want to type the ugly for-loop stuff to hide every div...

Functional programming is useful in everyday stuff like the above!

函数式编程在上面的日常事务中很有用!

(note: I don't know if my example fits the exact definition of functional programming, but if it does, than functional programming is awesome)

(注意:我不知道我的例子是否符合函数式编程的确切定义,但如果确实如此,那么函数式编程就很棒了)

#13


I came up with a little trick recently to make lambdas, passed into my extension methods look more F# ish. Here it goes:

我最近提出了一个小技巧来制作lambdas,传递给我的扩展方法看起来更像F#ish。它来了:

What I wanted to do was something like:

我想做的是:

3.Times(() => Console.WriteLine("Doin' it"));

Now the extension method for that is easily implemented:

现在,扩展方法很容易实现:

    public static void Times(this int times, Action action)
    {
        Enumerable.Range(1, times).ToList().ForEach(index => action());
    }

What I didn't like is that I am specifying the index here: ForEach(index => action()) although it never is used, so I replaced it with a _ and got ForEach(_ => action())

我不喜欢的是我在这里指定索引:ForEach(index => action())虽然它从未使用过,所以我用_替换它并得到ForEach(_ => action())

That's nice, but now I was motivated to have my calling code look similar

这很好,但现在我有动力让我的调用代码看起来很相似

(I never liked the "()" at the beginning of the lambda expression), so instead of: 3.Times(() => ...); I wanted 3.Times(_ => ...); The only way to implement this was to pass a fake parameter to the extension method, which never gets used and so I modified it like so:

(我从不喜欢lambda表达式开头的“()”),而不是:3.Times(()=> ...);我想要3.Times(_ => ...);实现这一点的唯一方法是将一个假参数传递给扩展方法,该方法永远不会被使用,所以我修改它是这样的:

    public static void Times(this int times, Action<byte> action)
    {
        Enumerable.Range(1, times).ToList().ForEach(_ => action(byte.MinValue));
    }

This allows me to call it like this:

这允许我像这样调用它:

3.Times(_ => Console.WriteLine("Doin' it"));

Not much of a change, but I still enjoyed, that it was possible to make that little tweak so easily and at the same time removing the "()" noise makes it much more readable.

没有太大的改变,但我仍然很享受,可以轻松地进行这种小调整,同时去除“()”噪音使其更具可读性。

#14


Not really answering the question, but this is a very good link for those who want to know about functional programming in C#

并没有真正回答这个问题,但对于那些想要了解C#中的函数式编程的人来说,这是一个非常好的链接

http://blogs.msdn.com/b/ericwhite/archive/2006/10/04/fp-tutorial.aspx

#15


  1. Show how to code a distinct of an array. Distinct is very easy in SQL but was hard on a memory array. Now it is easy to distinct an array with LINQ.

    演示如何编码不同的数组。 SQL中的区别非常简单,但在内存阵列上却很难。现在很容易用LINQ区分数组。

  2. You can explain them that there will be parralel LINQ (PLINQ) in the future. When you start writing functional code it will be easier to parralelize your application. Google uses MapReduce extensively.

    您可以向他们解释将来会有parralel LINQ(PLINQ)。当您开始编写功能代码时,您可以更轻松地对应用程序进行分类。 Google广泛使用MapReduce。

  3. Explain to them that LINQ is a query language to manipulate al different kinds of data. In memory, in a database, excell, web services, xml files, JSON files. It is some kind of universal SQL. However people who don't like SQL will be less convinced.

    向他们解释LINQ是一种操纵不同类型数据的查询语言。在内存中,在数据库中,excell,web服务,xml文件,JSON文件。它是某种通用SQL。然而,不喜欢SQL的人会更不相信。

I wouldn't talk to much about functional programming, I would explain how LINQ can help developers.

我不会谈论函数式编程,我会解释LINQ如何帮助开发人员。

#16


It's interesting noone has really answered the question: what task is best done in a "functional style"?

有趣的是,没有人真正回答过这个问题:什么样的任务最好用“功能风格”来完成?

A program/algorithm consists of 2 parts: logic control and data structure. I think the tasks best done in a functional style are those involved lists or arrays in cases where they behave like list (e.g. qsort). It's no coincidence that functional programming languages have very good support for lists.

程序/算法由两部分组成:逻辑控制和数据结构。我认为在功能样式中最好的任务是那些涉及列表或数组的行为,就像它们的行为类似于列表(例如qsort)。函数式编程语言对列表有很好的支持并非巧合。

When the data structures deviate from lists, I think the use of a functional programming style become a little "unnatural".

当数据结构偏离列表时,我认为函数式编程风格的使用变得有点“不自然”。

#1


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.

如果你刚刚发现函数式编程,我不建议尝试就这个主题进行权威性的讨论。我知道在我学习F#的前6个月里,我的所有代码都只是C#,语法稍微笨拙。然而,在那段时间之后,我能够以惯用的功能风格编写一致的优秀代码。

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

我建议你这样做:等待6个月左右,直到函数式编程风格更自然地出现,然后给你的演示文稿。

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#:

我向我所在地区的.NET用户组发了一个F#演示文稿,我小组中的很多人都对F#的模式匹配印象深刻。具体来说,我展示了如何遍历C#和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.

上面的代码是用惯用的C#风格编写的。它使用访客模式而不是类型测试来保证类型安全。这大概是218 LOC。

Here's the F# version:

这是F#版本:

#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.

这是65 LOC。由于它使用模式匹配而不是访问者模式,因此我们不会丢失任何类型安全性,并且代码非常易于阅读。

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

任何类型的符号处理都比F#更容易用F#写入。

[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:

[编辑添加:]哦,模式匹配不仅仅是访问者模式的替代品,它还允许您匹配数据的形状。例如,这是一个将Nand转换为等价的函数:

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#.

不可能在C#中简明扼要地编写这段代码。

#2


There are plenty examples out there but none will be as impact full as using a sample relevant to one of your projects at work. Examples like "Sum Of Squares" by Luca are awesome but if someone used that as proof as to how our code base could be written better I would not be convinced. All the example proves is some things are better wrote functionally. What you need to prove is your code base is better written functionally

有很多例子,但没有一个像使用与你工作的一个项目相关的样本那样充满影响力。像Luca的“Sum Of Squares”这样的例子很棒但是如果有人用它作为我们的代码库如何写得更好的证据我就不会相信。所有的例子证明有些东西在功能上写得更好。您需要证明的是您的代码库在功能上更好地编写

My advice would be to pick some popular trouble spots and some core spots in the code base, and rewrite them in a functional style. If you can demonstrate a substantially better solution, it will go a long way to winning over co-workers.

我的建议是选择一些流行的麻烦点和代码库中的一些核心点,并以功能样式重写它们。如果您能够展示出更好的解决方案,那么赢得同事将会有很长的路要走。

#3


Tasks for functional style? Any time you have a common coding pattern and want to reduce it. A while ago I wrote a bit on using C# for functional style, while making sure it's practical: Practical Functional C# (I'm hesitate to link to my own stuff here, but I think it's relevant in this case). If you have a common "enterprise" application, showing, say, how expressions are lovely in pattern matching won't be too convincing.

功能风格的任务?任何时候你有一个共同的编码模式,并希望减少它。不久之前,我写了一些关于使用C#的功能样式,同时确保它是实用的:实用功能C#(我很犹豫,在这里链接到我自己的东西,但我认为这在这种情况下是相关的)。如果您有一个共同的“企业”应用程序,那么显示表达式在模式匹配中的可爱程度将不会太令人信服。

But in real-world apps, there are TONS of patterns that pop up at a low, coding level. Using higher order functions, you can make them go away. As I show in that set of blog posts, my favourite example is WCF's "try-close/finally-abort" pattern. The "try/finally-dispose" pattern is so common it got turned into a language keyword: using. Same thing for "lock". Those are both trivially represented as higher order functions, and only because C# didn't support them originally do we need hard-coded language keywords to support them. (Quick: switch your "lock" blocks out to use a ReaderWriter lock. Oops, we'll have to write a higher order function first.)

但在现实世界的应用程序中,有很多模式会以较低的编码级别出现。使用更高阶的函数,你可以让它们消失。正如我在那篇博文中所展示的那样,我最喜欢的例子是WCF的“try-close / finally-abort”模式。 “try / finally-dispose”模式非常常见,它变成了一个语言关键字:using。 “锁定”也是一样。这些都被简单地表示为高阶函数,并且仅仅因为C#最初不支持它们,我们需要硬编码语言关键字来支持它们。 (快速:切换你的“锁定”块以使用ReaderWriter锁。糟糕,我们必须先写一个更高阶的功能。)

But perhaps convincing just requires looking at Microsoft. Generics aka parametric polymorphism? That's hardly OO, but a nice functional concept that, now, everyone loves. The cute Ninject framework wouldn't work without it. Lambdas? As expression trees, they're how LINQ, Fluent NHibernate, etc. get all their power. Again, that doesn't come from OO or imperative programming. The new Threading library? Pretty ugly without closures.

但也许令人信服只需要看微软。泛型又是参数多态?这不是OO,而是一个很好的功能概念,现在,每个人都喜欢。如果没有它,可爱的Ninject框架将无法运行。 Lambda表达式?作为表达树,他们是如何LINQ,Fluent NHibernate等获得他们所有的力量。同样,这不是来自OO或命令式编程。新的线程库?非常丑陋没有封闭。

So, functional programming has been blessing things like .NET over the last decade. The major advances (such as generics, "LINQ") are directly from functional languages. Why not realise there's something to it and get more involved in it? That's how I'd phrase it to skeptics.

因此,在过去十年中,函数式编程一直在为.NET之类的事物带来祝福。主要的进步(例如泛型,“LINQ”)直接来自函数式语言。为什么不意识到它有什么东西并且更多地参与其中?这就是我对怀疑论者的看法。

The bigger problem is actually getting people to make the jump in understanding to higher order functions. While it's quite easy, if you've never seen it before in your life, it might be shocking an incomprehensible. (Heck, seems like a lot of people think generics are just for type-safe collections, and LINQ is just embedded SQL.)

更大的问题实际上是让人们在理解更高阶函数方面有所突破。虽然它很容易,如果你以前从未见过它,那可能会令人难以理解。 (哎呀,似乎很多人认为泛型只是用于类型安全的集合,而LINQ只是嵌入式SQL。)

So, what you should do is go through your codebase, and find places that are an overly-complicated imperative nightmare. Search for the underlying patterns, and use functions to string it together nicely. If you can't find any, you might settle for just demo'ing off lists. For example "find all the Foos in this list and remove them". That's a 1 line thing in functional style "myList.Remove(x=>x.Bla > 0)" versus 7 lines in C# style (create a temp list, loop through and add to-remove items, loop though and remove the items).

所以,你应该做的就是浏览你的代码库,找到一个过于复杂的必要噩梦。搜索底层模式,并使用函数将它们很好地串在一起。如果你找不到任何东西,你可能会满足于只是演示关闭列表。例如,“在此列表中找到所有Foos并将其删除”。功能样式“myList.Remove(x => x.Bla> 0)”中的1行与C#样式中的7行相比(创建临时列表,循环并添加删除项,循环并删除项)。

The hope is that, even though the syntax is odd, people will recognize "wow, that's a lot simpler". If they can put down the "verbose == more readable" and "that looks confusing" for a bit, you'll have a chance.

希望是,即使语法很奇怪,人们也会认识到“哇,这更简单”。如果他们可以放下“详细= =更具可读性”和“看起来令人困惑”,你就有机会。

Good luck.

#4


Essentially, the functional paradigm is highly effective for parallel processing:

从本质上讲,功能范例对并行处理非常有效:

"The really interesting thing I want you to notice, here, is that as soon as you think of map and reduce as functions that everybody can use, and they use them, you only have to get one supergenius to write the hard code to run map and reduce on a global massively parallel array of computers, and all the old code that used to work fine when you just ran a loop still works only it's a zillion times faster which means it can be used to tackle huge problems in an instant.

“我想让你注意到的真正有趣的事情是,只要你想到map并将其缩减为每个人都可以使用的函数,并且他们使用它们,你只需要一个超级因子来编写硬代码来运行在全局大规模并行计算机阵列上进行映射和缩减,以及在运行循环时过去工作正常的所有旧代码仍然只能运行数十亿次,这意味着它可以用于瞬间解决大问题。

Lemme repeat that. By abstracting away the very concept of looping, you can implement looping any way you want, including implementing it in a way that scales nicely with extra hardware."

勒米重复一遍。通过抽象出循环的概念,你可以以任何你想要的方式实现循环,包括以一种可以与额外硬件很好地扩展的方式实现循环。

http://www.joelonsoftware.com/items/2006/08/01.html

#5


The best advocacy paper ever written for the functional style is a paper by John Hughes called Why Functional Programming Matters. I suggest you do some examples for yourself until you reach the stage where you can convincingly make the arguments laid out in that paper.

为功能风格编写的最佳宣传论文是John Hughes撰写的一篇名为Why Functional Programming Matters的论文。我建议你自己做一些例子,直到你达到可以令人信服地在论文中提出论点的阶段。

Many of the examples in the paper are numerical and do not resonate with today's audiences. One more contemporary exercise I gave my students was to use the ideas in that paper to pack large media files onto 4.7GB DVDs for backup. They used Michael Mitzenmacher's "bubble search" algorithm to generate alternative packings, and using this algorithm and Hughes's techniques it was easy to get each DVD (except the last) 99.9% full. Very sweet.

本文中的许多例子都是数字的,不会与今天的观众产生共鸣。我给学生们的另一个当代练习是使用那篇论文中的想法将大型媒体文件打包到4.7GB DVD上进行备份。他们使用Michael Mitzenmacher的“泡泡搜索”算法来生成替代包装,并且使用这种算法和Hughes的技术,很容易让每张DVD(除了最后一张)满99.9%。很甜。

#6


Another example would be the Quicksort algorithm. It can be described very briefly in a functional language like Haskell:

另一个例子是Quicksort算法。它可以用像Haskell这样的函数式语言进行简要描述:

qsort []     = []
qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++ qsort (filter (>= x) xs)

but needs some more coding in an iterative language. On the referenced Website you can also find a lot of other examples with comparisons between languages.

但需要在迭代语言中进行更多编码。在引用的网站上,您还可以找到许多其他语言比较的例子。

#7


In order to achieve what you want, and to communicate it to others in your organisation you need to demonstrate your company's business being built in a better way.

为了实现您的目标,并将其传达给组织中的其他人,您需要证明公司的业务是以更好的方式构建的。

Its no use using a couple of algorithms to demonstrate the power of functional programming if its totally useless for your business domain. So, take some existing code and rewrite it functionally. If you can prove through that, that it is better, people will listen to you - you've shown them a concrete, relevant example. IF you cannot, then perhaps functional programming is not the solution you've been looking for.

如果它对您的业务领域完全无用,那么使用几种算法来展示函数式编程的强大功能是没有用的。因此,请使用一些现有代码并在功能上重写它。如果你能证明这一点,那就更好了,人们会听你的 - 你已经向他们展示了一个具体的,相关的例子。如果你不能,那么函数式编程可能不是你一直在寻找的解决方案。

#8


If by the 'functional style' you mean the usage of concepts like 'map', 'apply', 'reduce', 'filter', lambda functions and list comprehensions, then it should be evident that code that has to deal with operations on lists is almost always more concise when written in the 'functional style'. But if you're mixing 'functional style' with imperative code in a mostly imperative language, it really is just a matter of style.

如果用'功能样式'表示使用'map','apply','reduce','filter',lambda函数和列表推导等概念,那么很明显那些必须处理操作的代码使用“功能样式”编写时,列表几乎总是更简洁。但是,如果你将“功能风格”与命令式代码混合在一种大多数命令式语言中,那真的只是风格问题。

In python for example, you can reimplement the Haskell qsort crackmigg posted like so:

例如,在python中,您可以重新实现Haskell qsort crackmigg,如下所示:

def qsort(list):
    if list == []:
        return []
    else:
        x = list[0]; xs = list[1:]
        return qsort(filter(lambda y: y<x, xs)) + [x] + qsort(filter(lambda y: y>= x, xs))

Although writing the last line as

虽然把最后一行写成了

return qsort([y for y in xs if y<x]) + [x] + qsort([y for y in xs if y>=x])

is arguably more 'pythonic'.

可以说更像是“pythonic”。

But this is obviously more concise than the implementation here:

但这显然比这里的实现简洁:

http://hetland.org/coding/python/quicksort.html

Which, incidentally, is how I would have thought about implementing it before I learned Haskell.

顺便提一下,在我学习Haskell之前,我是如何考虑实现它的。

The functional version is extremely clear if and only if you are acclimated to functional programming and grok what filter is going to do as easily as the well-worn C++ programmer groks a for loop without even having to think much about it. And that's clearly what the real issue at stake here: functional 'style' programming is a completely different mindset. If the people you work with aren't used to thinking recursively, and aren't the type to get excited about, not just a new technology, but a whole other way of thinking about solving their problems, then any amount of code comparisons isn't going to win them over.

功能版本非常清晰,当且仅当你适应了函数式编程并且很容易理解过滤器将要做的事情,就像老旧的C ++程序员在没有考虑它的情况下进行for循环一样容易。这显然是真正的问题所在:功能性“风格”编程是一种完全不同的心态。如果与你一起工作的人不习惯递归思考,并且不是那种兴奋的类型,不仅仅是一种新技术,而是另一种思考解决问题的方法,那么任何数量的代码比较都不是不会赢得他们。

#9


A good example cood be creating your own programming language using existing one, where you will have to use Monads.

一个很好的例子是使用现有的编程语言创建自己的编程语言,您必须使用Monads。

With F# it's much much simplier to write parsing logic than with C#.

使用F#,编写解析逻辑要比使用C#简单得多。

Take a look at this article: Functional .NET - LINQ or Language Integrated Monads?

看看这篇文章:功能.NET - LINQ或语言集成Monads?

#10


Algorithms involving backtracking search and simplifying undo support in GUIs are two places I've used functional style in practice.

涉及回溯搜索和简化GUI中撤消支持的算法是我在实践中使用功能样式的两个地方。

#11


A simple example of a task that is often easiest done in a functional style is the transformation of data from one form to another. "Sum of squares" is a trivial example of data transformation. Luca's talk at last year's PDC showed how to use this sort of data transformation for something more practical, downloading and analyzing stock quotes. The demo is done in F#, but the concepts are the same and can be applied to C# or most other programming languages.

通常在功能样式中最容易完成的任务的简单示例是将数据从一种形式转换为另一种形式。 “平方和”是数据转换的一个简单例子。 Luca在去年的PDC演讲中展示了如何使用这种数据转换来实现更实用,下载和分析股票报价。演示在F#中完成,但概念是相同的,可以应用于C#或大多数其他编程语言。

http://channel9.msdn.com/pdc2008/TL11/

#12


Show them jQuery's way of iterating over DOM elements:

向他们展示jQuery迭代DOM元素的方式:

$(".magic-divs").click(function(){
    // FYI, in this context, "this" will be the element clicked on.
    alert("somebody clicked on " + this.id);
    this.hide();

});

$(".magic-divs").show();

vs. how most google results for "javascript element by classname" do it:

与大多数谷歌搜索“javascript element by classname”的结果相比:

var arrayOfElements = // this is filled with the elements somehow
for(var i=0,j=arrayOfElements.length; i<j; i++) {
   alert("now I get to add an onclick event somehow " + i);
}
// i dont even want to type the ugly for-loop stuff to hide every div...

Functional programming is useful in everyday stuff like the above!

函数式编程在上面的日常事务中很有用!

(note: I don't know if my example fits the exact definition of functional programming, but if it does, than functional programming is awesome)

(注意:我不知道我的例子是否符合函数式编程的确切定义,但如果确实如此,那么函数式编程就很棒了)

#13


I came up with a little trick recently to make lambdas, passed into my extension methods look more F# ish. Here it goes:

我最近提出了一个小技巧来制作lambdas,传递给我的扩展方法看起来更像F#ish。它来了:

What I wanted to do was something like:

我想做的是:

3.Times(() => Console.WriteLine("Doin' it"));

Now the extension method for that is easily implemented:

现在,扩展方法很容易实现:

    public static void Times(this int times, Action action)
    {
        Enumerable.Range(1, times).ToList().ForEach(index => action());
    }

What I didn't like is that I am specifying the index here: ForEach(index => action()) although it never is used, so I replaced it with a _ and got ForEach(_ => action())

我不喜欢的是我在这里指定索引:ForEach(index => action())虽然它从未使用过,所以我用_替换它并得到ForEach(_ => action())

That's nice, but now I was motivated to have my calling code look similar

这很好,但现在我有动力让我的调用代码看起来很相似

(I never liked the "()" at the beginning of the lambda expression), so instead of: 3.Times(() => ...); I wanted 3.Times(_ => ...); The only way to implement this was to pass a fake parameter to the extension method, which never gets used and so I modified it like so:

(我从不喜欢lambda表达式开头的“()”),而不是:3.Times(()=> ...);我想要3.Times(_ => ...);实现这一点的唯一方法是将一个假参数传递给扩展方法,该方法永远不会被使用,所以我修改它是这样的:

    public static void Times(this int times, Action<byte> action)
    {
        Enumerable.Range(1, times).ToList().ForEach(_ => action(byte.MinValue));
    }

This allows me to call it like this:

这允许我像这样调用它:

3.Times(_ => Console.WriteLine("Doin' it"));

Not much of a change, but I still enjoyed, that it was possible to make that little tweak so easily and at the same time removing the "()" noise makes it much more readable.

没有太大的改变,但我仍然很享受,可以轻松地进行这种小调整,同时去除“()”噪音使其更具可读性。

#14


Not really answering the question, but this is a very good link for those who want to know about functional programming in C#

并没有真正回答这个问题,但对于那些想要了解C#中的函数式编程的人来说,这是一个非常好的链接

http://blogs.msdn.com/b/ericwhite/archive/2006/10/04/fp-tutorial.aspx

#15


  1. Show how to code a distinct of an array. Distinct is very easy in SQL but was hard on a memory array. Now it is easy to distinct an array with LINQ.

    演示如何编码不同的数组。 SQL中的区别非常简单,但在内存阵列上却很难。现在很容易用LINQ区分数组。

  2. You can explain them that there will be parralel LINQ (PLINQ) in the future. When you start writing functional code it will be easier to parralelize your application. Google uses MapReduce extensively.

    您可以向他们解释将来会有parralel LINQ(PLINQ)。当您开始编写功能代码时,您可以更轻松地对应用程序进行分类。 Google广泛使用MapReduce。

  3. Explain to them that LINQ is a query language to manipulate al different kinds of data. In memory, in a database, excell, web services, xml files, JSON files. It is some kind of universal SQL. However people who don't like SQL will be less convinced.

    向他们解释LINQ是一种操纵不同类型数据的查询语言。在内存中,在数据库中,excell,web服务,xml文件,JSON文件。它是某种通用SQL。然而,不喜欢SQL的人会更不相信。

I wouldn't talk to much about functional programming, I would explain how LINQ can help developers.

我不会谈论函数式编程,我会解释LINQ如何帮助开发人员。

#16


It's interesting noone has really answered the question: what task is best done in a "functional style"?

有趣的是,没有人真正回答过这个问题:什么样的任务最好用“功能风格”来完成?

A program/algorithm consists of 2 parts: logic control and data structure. I think the tasks best done in a functional style are those involved lists or arrays in cases where they behave like list (e.g. qsort). It's no coincidence that functional programming languages have very good support for lists.

程序/算法由两部分组成:逻辑控制和数据结构。我认为在功能样式中最好的任务是那些涉及列表或数组的行为,就像它们的行为类似于列表(例如qsort)。函数式编程语言对列表有很好的支持并非巧合。

When the data structures deviate from lists, I think the use of a functional programming style become a little "unnatural".

当数据结构偏离列表时,我认为函数式编程风格的使用变得有点“不自然”。