挑战:柯里化或部分应用 C#4 字符串的更简洁方法。Join

发布于 2024-08-29 02:32:23 字数 2116 浏览 8 评论 0原文

背景

我最近读到,.NET 4 的 System.String 类有一个新的 Join 方法重载。这个新的重载需要一个分隔符和一个 IEnumerable,它允许将任意集合连接到单个字符串中,而无需转换为中间字符串数组。

凉爽的!这意味着我现在可以这样做:

var evenNums = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0);
var list = string.Join(",",evenNums);

...而不是这样:

var evenNums = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .Select(i => i.ToString())
    .ToArray();
var list = string.Join(",", evenNums);

...从而节省将每个项目转换为字符串,然后分配数组的时间。

问题

然而,作为一般编程风格的粉丝,特别是 C# 中方法链接的粉丝,我更希望能够编写如下内容:

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .string.Join(",");

尽管这不是合法的 C#。是的,我可以这样做与Enumerable.Aggregate,是的,我可以用我自己的 Join 扩展方法来做),但这些方法很难阅读/效率低下,感觉像是一种逃避(分别),所以我想尝试并以不同的方式来做。到目前为止我设法得到的最接近的是:

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .ApplyTo(
        Functional.Curry<string, IEnumerable<object>, string>
            (string.Join)(",")
    );

...使用以下扩展方法:

public static class Functional
{
    public static TRslt
    ApplyTo<TArg, TRslt>(this TArg arg, Func<TArg, TRslt> func)
    {
        return func(arg);
    }

    public static Func<T1, Func<T2, TResult>>
    Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
    {
        Func<Func<T1, T2, TResult>, Func<T1, Func<T2, TResult>>> curried
            = f => x => y => f(x, y);
        return curried(func);
    }
}

这非常冗长,需要显式定义参数和字符串的返回类型。我想使用的Join重载,并且依赖基于 C#4 的方差特性,因为我们将参数之一定义为 IEnumerable 而不是 IEnumerable。

挑战

您能否找到一种更简洁的方法来使用编程的方法链风格来实现这一目标?

这个挑战是试图在 C# 中找到一种简洁的方法来柯里化具有多个重载的函数 - 这只是为了好玩!

Background

I recently read that .NET 4's System.String class has a new overload of the Join method. This new overload takes a separator, and an IEnumerable<T> which allows arbitrary collections to be joined into a single string without the need to convert to an intermediate string array.

Cool! That means I can now do this:

var evenNums = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0);
var list = string.Join(",",evenNums);

...instead of this:

var evenNums = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .Select(i => i.ToString())
    .ToArray();
var list = string.Join(",", evenNums);

...thus saving on a conversion of every item to a string, and then the allocation of an array.

The Problem

However, being a fan of the functional style of programming in general, and method chaining in C# in particular, I would prefer to be able to write something like this:

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .string.Join(",");

This is not legal C# though. Yes, I could do it with Enumerable.Aggregate, and yes, I could do it with my own Join extension method), but those approaches are hard to read/inefficient and feel like a cop-out (respectively) so I would like to try and do it a different way. The closest I've managed to get so far, is this:

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .ApplyTo(
        Functional.Curry<string, IEnumerable<object>, string>
            (string.Join)(",")
    );

...using the following extension methods:

public static class Functional
{
    public static TRslt
    ApplyTo<TArg, TRslt>(this TArg arg, Func<TArg, TRslt> func)
    {
        return func(arg);
    }

    public static Func<T1, Func<T2, TResult>>
    Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
    {
        Func<Func<T1, T2, TResult>, Func<T1, Func<T2, TResult>>> curried
            = f => x => y => f(x, y);
        return curried(func);
    }
}

This is quite verbose, requires explicit definition of the parameters and return type of the string.Join overload I want to use, and relies upon C#4's variance features because we are defining one of the arguments as IEnumerable rather than IEnumerable.

The Challenge

Can you find a neater way of achieving this using the method-chaining style of programming?

This challenge is about trying to find a terse way, in C#, to curry a function that has multiple overloads - and it's just for fun!

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

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

发布评论

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

评论(3

森林很绿却致人迷途 2024-09-05 02:32:23

有什么问题:

var list = string.Join(",",Enumerable.Range(1, 100).Where(i => i%2 == 0));

我真的不明白这为什么不是函数式编程。这是更少的连锁,确实如此。但函数式编程不是关于链接,而是关于编写更多声明性语句,这在我的书中就是这样做的。当然,如果你真的希望它被链接起来,你可以这样做: 什么是 LINQ 方式来内爆/连接字符串数组?

What's wrong with:

var list = string.Join(",",Enumerable.Range(1, 100).Where(i => i%2 == 0));

I don't really see how this is not functional programming. It's less chaining, true. But functional program is not about chaining, it's about writing more declarative statements, which this is doing in my book. Of course, if you really want it to be chained you can do this: What is the LINQ way to implode/join a string array?

原野 2024-09-05 02:32:23

我不确定为什么您需要示例中的附加 Curry 方法。只需使用另一个 Lambda 即可产生更简洁的选项。

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .ApplyTo((x) => { return string.Join(",", x); })

I'm not sure why you need the additional Curry method from your example. Simply using another Lambda produces a more terse option.

var list = Enumerable.Range(1, 100)
    .Where(i => i%2 == 0)
    .ApplyTo((x) => { return string.Join(",", x); })
迷荒 2024-09-05 02:32:23

五年过去了,这个问题仍然没有答案,让我们试试吧!

高效,无需外部代码,完全链接且可读:

var list = Enumerable.Range(1, 100000)
    .Where(i => i % 2 == 0)
    .Aggregate(new StringBuilder(), (prev, i) => prev.AppendFormat(",{0}",i))
    .Remove(0,1)
    .ToString();

After five years it's still unanswered, let's try this!

Efficient, with no external code, completely chaining and readable:

var list = Enumerable.Range(1, 100000)
    .Where(i => i % 2 == 0)
    .Aggregate(new StringBuilder(), (prev, i) => prev.AppendFormat(",{0}",i))
    .Remove(0,1)
    .ToString();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文