C# 中柯里化的优点是什么? (实现部分功能)

发布于 2024-08-24 11:36:18 字数 51 浏览 6 评论 0原文

C# 中柯里化的优点是什么?

在柯里化函数上实现部分函数应用有什么好处?

What is the advantage of Currying in C#?

What is the advantage of achieving partial function application on a curried function?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

浮世清欢 2024-08-31 11:36:18

如果您的问题是如何在 C# 中实现柯里化,这里有一个示例

public Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(Func<T1, T2, TResult> func)
    {
        return p1 => p2 => func(p1, p2);
    }

柯里化可以用任何支持闭包(lambda)的语言来实现,并且对于部分函数应用程序很有用,例如在 UI 编程中,其中执行函数所需的所有输入未收到,因此传递了一个柯里化函数,并在其中捕获了已接收到的输入。

If your question was how to implement currying in C# , here is an example

public Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(Func<T1, T2, TResult> func)
    {
        return p1 => p2 => func(p1, p2);
    }

Currying can be implemented in any language that supports closures(lambdas), and is useful for partial function application like in UI programming where all the input necessary for the execution of function isnt received, so a curried function is passed around with already received inputs captured in it.

£冰雨忧蓝° 2024-08-31 11:36:18

来自 维基百科

柯里化实际上并不是很
与我们做的事情不同
计算给定的函数
一张纸上的值。

<块引用>

取函数f(x,y) = y / x

要计算 f(2,3),首先将 x 替换为 2

由于结果是 y 中的一个新函数,因此该函数 g(y) 可以定义为

g(y) = f(2,y) = y / 2

接下来,将 y 参数替换为 3

提供结果,g(3) = f(2,3) = 3 / 2

在纸面上,使用经典符号,
只是我们似乎都在做这一切
同时。但事实上,当
替换一块上的参数
纸,按顺序完成
(即部分)。每次更换
结果在一个函数内
功能。当我们依次替换
每个参数,我们都柯里化
功能变得越来越简单
原始版本。最终,
我们最终得到了一系列函数
在 lambda 演算中,每个
函数只接受一个参数,并且
多参数函数通常是
以柯里化形式表示。

柯里化的实际动机
是很多时候的功能
通过提供一些但不是全部获得
柯里化函数的参数
(通常称为部分应用)是
有用;例如,许多语言
有一个类似的函数或运算符
加一。柯里化使得很容易
定义这些函数。

From Wikipedia

Currying is actually not very
different from what we do when we
calculate a function for some given
values on a piece of paper.

Take the function f(x,y) = y / x

To evaluate f(2,3), first, replace x with 2.

Since the result is a new function in y, this function g(y) can be defined as

g(y) = f(2,y) = y / 2

Next, replacing the y argument with 3,

provides the result, g(3) = f(2,3) = 3 / 2.

On paper, using classical notation,
it's just that we seem to do it all at
the same time. But, in fact, when
replacing arguments on a piece of
paper, it is done sequentially
(i.e.partially). Each replacement
results in a function within a
function. As we sequentially replace
each argument, we are currying the
function into simpler and simpler
versions of the original. Eventually,
we end up with a chain of functions as
in lambda calculus, where each
function takes only one argument, and
multi-argument functions are usually
represented in curried form.

The practical motivation for currying
is that very often the functions
obtained by supplying some but not all
of the arguments to a curried function
(often called partial application) are
useful; for example, many languages
have a function or operator similar to
plus_one. Currying makes it easy to
define these functions.

一身软味 2024-08-31 11:36:18

我发现当我想重用代码时,部分函数应用程序(与柯里化相反)非常有用。

需要明确的是,由于柯里化和偏函数应用的定义似乎变得模糊,所以偏函数应用是指采用具有 N 个参数的函数,并将其转换为具有 N-1 个参数的函数。

特别是,在编写单元测试时它非常方便。由于我将编写数百个单元测试,因此我尝试尽可能重用测试代码。因此,我可能有一个通用的测试方法,它接受我想要测试的方法的委托,以及该方法的一些参数和预期结果。常见的测试方法将使用提供的参数执行被测方法,并将有多个断言将结果与预期结果进行比较。

当我想要测试一个方法,该方法的参数多于传递到通用测试方法的委托时,问题就出现了。我可以编写另一个与第一个相同的通用测试方法,除了采用具有不同签名的委托之外。然而,这似乎是在重复我自己。为了避免编写此类重复代码,我可以使用部分函数应用程序将采用两个参数的委托转换为采用单个参数的委托。现在我可以使用常用的测试方法来测试采用一个或两个参数的方法。

这是我用来修复传入委托的参数之一的辅助方法之一:

/// <summary>
/// Fixes an argument of an action delegate, creating a closure that combines the 
/// delegate and the argument value. 
/// </summary>
/// <returns>An action delegate which takes only one argument.</returns>
public static Action<TIn1> FixActionArgument<TIn1, TIn2>(Action<TIn1, TIn2> action, 
    TIn2 argumentValue)
{
    return in1 => action(in1, argumentValue);
}

I've found partial function application, as opposed to currying, to be useful when I want to reuse code.

To be clear, since the definitions of currying and partial function application seem to get blurred, by partial function application I mean taking a function with N parameters, and converting it into a function with N-1 parameters.

In particular, it's been handy when writing unit tests. Since I'll be writing hundreds of unit tests I try to reuse test code wherever possible. So I may have a common test method that takes a delegate to a method I wish to test, plus some parameters to that method and an expected result. The common test method will execute the method under test with the supplied parameters, and will have several assertions comparing the result to the expected result.

The problem comes when I want to test a method that has more parameters than the delegate being passed into the common test method. I could write another common test method which is identical to the first one, apart from taking a delegate with a different signature. That seems like repeating myself, however. To avoid having to write such duplicate code, I can use partial function application to convert a delegate taking, say, two parameters, into a delegate taking a single parameter. Now I can use my common test method to test methods that take either one or two parameters.

Here's one of the helper methods I use to fix one of the arguments of the delegate that was passed in:

/// <summary>
/// Fixes an argument of an action delegate, creating a closure that combines the 
/// delegate and the argument value. 
/// </summary>
/// <returns>An action delegate which takes only one argument.</returns>
public static Action<TIn1> FixActionArgument<TIn1, TIn2>(Action<TIn1, TIn2> action, 
    TIn2 argumentValue)
{
    return in1 => action(in1, argumentValue);
}
贵在坚持 2024-08-31 11:36:18

C# 中柯里化的优点是它允许 C# 开发人员以函数式编程风格进行开发。

想想 LINQ。 LINQ 查询允许您传入方法作为参数:

someCollection.Where(x => x.someVal == 1);

x.someVal == 1 作为函数进行计算,然后 Where 在其自身的执行中使用返回值。

这是大多数 .NET 3 开发人员都熟悉的示例,但很少有人意识到他们正在涉足函数编程。如果没有 Curry 能力,LINQ 就不可能实现。

...希望这能弥补我自作聪明的评论。

The advantage of Currying in C# is that it allows C# developers to develop in a Functional Programming style.

Think about LINQ. A LINQ query allows you to pass in a method as a parameter:

someCollection.Where(x => x.someVal == 1);

x.someVal == 1 gets evaluated as a function and then Where uses the return value in its own execution.

It's an example that most .NET 3 developers are familiar with, but few realize that they're dabbling in Function Programming. Without the ability to Curry, LINQ wouldn't be possible.

...hopefull that makes up for my smart-ass comment.

筱果果 2024-08-31 11:36:18

一个简单的柯里化将是

using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {

            Func<double, double, double, double, double> newTonLaw = (m1, m2, r, g) => ((m1 * m2) / Math.Pow(r,2)) * g;

            // Mass of Earth= 5.98 * 10e24 , Gravitational Constant = 6.6726 * 10e-11
            Func<double, double, double> onEarth = (m2, r) => newTonLaw.Invoke(5.98 * 10e24, m2, r, 6.6726*10e-11);

            // Mass of Moon= 7.348x10e22 , Gravitational Constant = 6.6726 * 10e-11
            Func<double, double, double> onMoon = (m2, r) => newTonLaw.Invoke(7.348 * 10e22, m2, r, 6.6726 * 10e-11);

            Trace.WriteLine(onEarth(70, 6.38 * 10e6)); // result 686.203545562642
            Trace.WriteLine(onMoon(70, 6.38 * 10e6)); // result 8.43181212841855
        }
    }
}

A simple Currying will be

using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
    class Program
    {
        static void Main(string[] args)
        {

            Func<double, double, double, double, double> newTonLaw = (m1, m2, r, g) => ((m1 * m2) / Math.Pow(r,2)) * g;

            // Mass of Earth= 5.98 * 10e24 , Gravitational Constant = 6.6726 * 10e-11
            Func<double, double, double> onEarth = (m2, r) => newTonLaw.Invoke(5.98 * 10e24, m2, r, 6.6726*10e-11);

            // Mass of Moon= 7.348x10e22 , Gravitational Constant = 6.6726 * 10e-11
            Func<double, double, double> onMoon = (m2, r) => newTonLaw.Invoke(7.348 * 10e22, m2, r, 6.6726 * 10e-11);

            Trace.WriteLine(onEarth(70, 6.38 * 10e6)); // result 686.203545562642
            Trace.WriteLine(onMoon(70, 6.38 * 10e6)); // result 8.43181212841855
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文