【问题标题】:Shuffling a Stack<T>洗牌堆栈<T>
【发布时间】:2015-11-11 02:06:26
【问题描述】:
    public static void Shuffle<T> ( this Stack<T> stack )
    {
        List<T> list = stack.ToList ();
        list.Shuffle ();
        stack = list.ToStack ();
    }

    public static void Shuffle<T> ( this List<T> list )
    {
        for ( int i = 0; i < list.Count; i++ )
        {
            int num = Form1.rnd.Next ( list.Count );
            T temp = list[i];
            list[i] = list[num];
            list[num] = temp;
        }
    }

    public static Stack<T> ToStack<T> ( this List<T> list )
    {
        Stack<T> stack = new Stack<T> ();
        foreach ( T t in list )
            stack.Push ( t );

        return stack;
    }

以上是我对通用堆栈进行洗牌的尝试。然而,虽然 List Shuffle 扩展方法有效,但 Stack Shuffle 并没有按预期工作。就好像根本没有调用中间的 Shuffle 调用。相反,它保持相同的列表,未打乱。所以我认为问题出在 ToStack 函数中。有人可以解释我的错误吗?提前致谢!

【问题讨论】:

  • 参数类型不应该是refout 以让调用者看到您对输入stack 参数的更改吗?
  • 还有 Nick,如果您在循环开始时创建一个新的 random 实例 var rnd = new Random() 然后 int num = rnd.Next ( list.Count );,您将能够摆脱对 Form1 的依赖并使用这些方法任何地方。
  • @ray,您不能在扩展方法的第一个参数上使用refout。如果他想使用refout,OP 必须将这些方法转换为普通方法。
  • @YacoubMassad:感谢您的澄清。阅读更多关于扩展方法的内容,因为我没有使用它们,而且开头的 this 对我来说很奇怪。

标签: c# list generics stack shuffle


【解决方案1】:

Shuffle 方法(采用 Stack&lt;T&gt;)按值获取堆栈参数。因此,当您调用 stack = list.ToStack (); 时,您正在更改该方法的本地变量 (stack)。

一种解决方案是像您对 List&lt;T&gt; shuffle 方法所做的那样:

public static Stack<T> Shuffle<T>(this Stack<T> stack)
{
    List<T> list = stack.ToList();
    list.Shuffle();
    return list.ToStack();
}

此方法将堆栈作为输入,以生成新的打乱堆栈。你可以这样使用它:

Stack<int> stack = new Stack<int>();
for(int i = 0 ; i < 10 ; i++)
    stack.Push(i);

stack = stack.Shuffle();

【讨论】:

  • 我认为public static void Shuffle&lt;T&gt; ( this List&lt;T&gt; list ) 如果是public static IEnumerable&lt;T&gt; Shuffle&lt;T&gt; ( this List&lt;T&gt; list ) 会返回洗牌后的列表会更好。
  • 但是stack参数难道不是对Stack实例的引用吗,因为它是一个扩展方法?
  • 扩展方法只是静态方法的巧妙语法糖。 this 允许这种魔法,但不会使本地停止表现得像方法中的本地一样。
  • 这更有意义。谢谢!
【解决方案2】:

问题是您将Stack&lt;T&gt; 的新实例分配给局部变量,这意味着调用堆栈变量没有更改。

现在您可以在参数中添加ref,但我通常会尽量避免这样做。

这里有两种选择。

如果您想继续使用相同的参考,那么您可以这样做:

public static void Shuffle<T>(this Stack<T> stack)
{
    var values = stack.ToArray();
    stack.Clear();
    foreach (var value in values.OrderBy(x => rnd.Next()))
        stack.Push(value);
}

这相当简单,无需调用单独的方法来洗牌。

或者,您可以返回Stack&lt;T&gt; 的新实例。

public static Stack<T> Shuffle<T>(this Stack<T> stack)
{
    return new Stack<T>(stack.OrderBy(x => rnd.Next()));
}

这再次避免了调用单独的 shuffle 方法的需要。

这两种方法都使用.OrderBy(x =&gt; rnd.Next()) 来执行随机播放,这比交换索引要容易得多。

【讨论】:

  • 旁注:OrderBy 比问题中显示的 Fisher-Yates shuffle 慢...但绝对更容易做对:)。
  • 非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2016-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-03
  • 1970-01-01
  • 2021-06-14
  • 1970-01-01
相关资源
最近更新 更多