开发者

Challenge: Neater way of currying or partially applying C#4's string.Join

开发者 https://www.devze.com 2022-12-27 10:52 出处:网络
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 collection

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!


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?


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


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();
0

精彩评论

暂无评论...
验证码 换一张
取 消