锯齿状数组转换为多维数组

4
我和我的同事正在进行一系列编码挑战。然而,我们希望使事情变得更有趣,并将答案缩小(是的,我知道,C#不是最适合缩小代码的语言)。
我们最近的挑战涉及旋转一个立方体(int[,])。所以最终我们打了个赌,如果我能用一行代码完成,他请我吃午餐,反之亦然。
在许多退格和我母亲不愿听到的话后,我终于想到了,或者我认为是这样。
我最后得到的是int[][]。
就我所知,没有简单的方法将其转换为int[,]. 所以我想问一下,是否真的可以在一行内完成转换。

编辑1

我想发布我的源代码,但是我的同事可能会发现它,我可能会失去免费的午餐。
3个回答

7
你至少需要两行代码,一行用于声明结果数组,另一行用于实际复制数据。
你可以先将数组扁平化为一个单一的数组,然后使用 Buffer.BlockCopy 将所有数据复制到结果数组中。
以下是示例:
var source = new int[][] {
    new int[4]{1,2,3,4},
    new int[4]{5,6,7,8},
    new int[4]{1,3,2,1},
    new int[4]{5,4,3,2}
};

var expected = new int[4,4] {
    {1,2,3,4},
    {5,6,7,8},
    {1,3,2,1},
    {5,4,3,2}
};

var result = new int[4, 4];
// count = source.Length * source[0].Length * sizeof(int) = 64, since BlockCopy is byte based
// to be dynamically you could also use System.Runtime.InteropServices.Marshal.SizeOf(source[0][0]) instead of sizeof(int)
Buffer.BlockCopy(source.SelectMany(r => r).ToArray(), 0, result, 0, 64);

result.Dump("result");
expected.Dump("expected");

结果:

enter image description here

如果你坚持要有些花哨的话:你可以使用委托动态调用 BlockCopy,让该委托返回 Object,以便将其用于匿名类的赋值,这显然符合你规则的精神,并将所有内容包装到集合中,以便得出像这样的单行怪兽代码:
var result = new[]{ new int[4, 4] }.Select(x => new { r = x, tmp = Delegate.CreateDelegate(typeof(Action<Array, int, Array, int, int>), typeof(Buffer).GetMethod("BlockCopy")).DynamicInvoke(new Object[]{source.SelectMany(r => r).ToArray(), 0, x, 0, 64})}).First().r;

3

你应该尝试学习JavaScript(我正在使用的是库中非常常见的模式)。

int[][] jagged = new[] { new[] { 1, 2, 3, 4, 5 }, new[] { 6, 7, 8, 9, 10 } };

int[,] array = ((Func<int[][], int[,]>)(x =>
{
    int[,] temp = new int[x.Length, x.Length != 0 ? x[0].Length : 0];
    for (int i = 0; i < x.Length; i++)
    {
        for (int j = 0; j < x[0].Length; j++)
        {
            temp[i, j] = x[i][j];
        }
    }

    return temp;
}))(jagged);
int[,] array =可以在去除行末换行符后变成单行。我声明了一个匿名方法并调用它(看到(jagged)了吗?那是我调用匿名方法的方式)。 单语句版本
int[,] array = jagged.Length == 0 ? 
    new int[0,0] : 
    jagged.SelectMany(x => x)
        .Select((x, ix) => new
        {
            i = ix / jagged[0].Length,
            y = ix % jagged[0].Length,
            val = x
        })
        .Aggregate(new int[jagged.Length, jagged[0].Length], (md, x) => (md[x.i, x.y] = x.val) == x.val ? md : null);

这里我正在使用Aggregate()方法。 TSeed是目标多维数组。注意(md[x.i,x.y] = x.val) == x.val ? md:null:我需要分配md[x.i,x.y] = x.val,但我需要返回md(因为Aggregate要求函数以这种方式工作)。 我进行了无用的检查(md[x.i,x.y] = x.val) == x.val并使用三元运算符返回md
请注意,我正在关闭一个变量(jagged),但可以删除该闭包(我认为)....嗯,似乎很复杂。

从技术上讲,我可以通过在每行结尾处按删除键,将任何C#程序变成一行代码。尝试学习JS,但并没有吸引我。 - Darkestlyrics
@Darkestlyrics 是的...但这仍然是一个单一的赋值语句...它不是在玩弄文字。这段代码和执行var enu = coll.Select(y => { return y; })之间没有实质性的区别。 - xanatos
好的,让我来试着用这个而不是LINQ玩一下。 - Darkestlyrics
@Darkestlyrics 这里的问题是你用行来定义了赌注... 你应该用语句来定义赌注。 - xanatos
@Darkestlyrics,但正如你所说,在“一行程序”中,您可以放置任何不使用预处理器指令(#something)的C#程序,因此您的问题已经自动解决。 - xanatos
显示剩余3条评论

0
如果您被允许使用 Func<> 和 lambda,那么您肯定可以这样做,并创建一个通用扩展来转换您调用它的对象。
/// <typeparam name="T">Output type</typeparam>
/// <typeparam name="U">Calling type</typeparam>
/// <param name="obj">object to pipe</param>
/// <param name="func">blackbox function</param>
/// <returns>whatever</returns>
public static T ForThis<T,U> (this U obj, Func<U,T> func)
{
    return func(obj);
}

通过以下方式,您应该能够将 int[,] 转换为 int[][]:

int[][] output = input.ForThis<int[][], int[,]>((obj) =>
{
    // transform obj == input of type int[,] into int[][]
    throw new NotImplementedException();
});

虽然我承认这个解决方案感觉像作弊,因为你只是将多行转换包装成一个lambda函数。


1
是的,我们看过这个,都认为这有点作弊。但还是感谢您的回答。 - Darkestlyrics

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接