c#通过引用传递Fisher-Yates Shuffler

本文关键字:Fisher-Yates Shuffler 引用 | 更新日期: 2023-09-27 18:06:29

我正在尝试使用Fisher-Yates算法来洗牌一堆元素。我在通过引用传递堆栈时遇到了麻烦。下面的代码给出了错误"迭代器不能有refor out形参"。我如何让算法对传入的实际堆栈起作用?

谢谢。

代码如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
public static class Doshuffle
{
    public static IEnumerable<T> Shuffle<T>(ref Stack<T> source)
    {
        Random rng = new Random();
        T[] elements = source.ToArray();
        source.Clear();
        // Note i > 0 to avoid final pointless iteration
        for (int i = elements.Length - 1; i > 0; i--)
        {
            // Swap element "i" with a random earlier element it (or itself)
            int swapIndex = rng.Next(i + 1);
            T tmp = elements[i];
            elements[i] = elements[swapIndex];
            elements[swapIndex] = tmp;
        }
        // Lazily yield (avoiding aliasing issues etc)
        foreach (T element in elements)
        {
            source.Push(element);
            yield return element;
        }
    }
}

}

c#通过引用传递Fisher-Yates Shuffler

我如何让算法对传递的实际堆栈起作用在吗?

不需要这里的ref参数,因为Stack<T>是一个引用类型,您不试图重新分配引用本身。

引用在默认情况下是按值传递的,但是这个值(引用)指向堆上的同一个对象,换句话说,你有两个引用指向同一个对象,这很好-所有的操作都将在原始Stack<T>对象上执行。

编辑:

根据你的评论,我建议你重新设计,不要修改原来的Stack<T>,这是麻烦的开始:

public static IEnumerable<T> Shuffle<T>(Stack<T> source)
{
    Random rng = new Random();
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length - 1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        T tmp = elements[i];
        elements[i] = elements[swapIndex];
        elements[swapIndex] = tmp;
    }
    // Lazily yield (avoiding aliasing issues etc)
    foreach (T element in elements)
    {
        yield return element;
    }
}

现在你可以这样使用:

foreach (var item in Doshuffle.Shuffle(gameDeck)) 
{ 
   System.Console.WriteLine(item.cardName); 
}

也要小心使用Random -您可能想要传递它。在这一点上,您可以使用Jon Skeet的Shuffle实现而不是您自己的——重用比重新发明要好。

最后编辑:

看起来你只是想打乱你的Stack<T>到位-使用扩展方法代替:

public static void Shuffle<T>(this Stack<T> source)
{
    Random rng = new Random();
    T[] elements = source.ToArray();
    source.Clear();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length - 1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        T tmp = elements[i];
        elements[i] = elements[swapIndex];
        elements[swapIndex] = tmp;
    }
    foreach (T element in elements)
    {
        source.Push(element);
    }
}

现在你可以这样做:

gameStack.Shuffle();

堆栈不需要像这样通过引用传递,因为引用类型(如Stack<T>)已经通过引用传递了。在引用形参上使用refout修饰符的唯一原因是,如果您想实际修改引用本身(例如创建一个新的Stack<T>并将其赋值给该形参作为一种替代返回方法——与C中的双指针相同)。

由于Stack是一个类,我认为在这种情况下不需要ref关键字。