【问题标题】:While Loop with assignment in C# Linq在 C# Linq 中进行赋值的 While 循环
【发布时间】:2012-12-28 13:39:23
【问题描述】:

我想为变量vStreamID 分配一个随机数。只要我的字典md_StreamDict包含生成的数字,这个数字就应该是新生成的。

长版:

vStreamID = (new Random()).Next(1000, 9999).ToString();
while (md_StreamDict.ContainsKey(vStreamID)) {
    vStreamID = (new Random()).Next(1000, 9999).ToString();
}

我想看看 LINQ 风格的东西

md_StreamDict.ContainsKey(vStreamID)
    .while( x => x = (new Random())
    .Next(1000, 9999)
    .ToString();

我知道上面的例子是香蕉。但是,如果有真正的方法可以实现这一目标,我会很高兴。不,我们不会再次开始通常的关于可读性的讨论。 ;)

【问题讨论】:

  • 如果你使用do { } while ();,即使是长版本也可能会更短:-)
  • 所以你只需要一个确定音调的数字,而这个数字不应该已经在字典中,为什么你需要 Random 呢?
  • 我认为 Neurodefect 需要在他的字典中添加一个新项,其中的键是随机的,并且字典中不存在。为什么是 linq,不知道..
  • 永远不要多次致电new Random()。只需创建一个Random 实例,然后重用该随机数生成器。否则,您可能会创建相同的Random 实例(从一个实例到下一个实例的时间可能没有变化),然后.Next(...) 将为每个Random 实例提供相同的编号。
  • 如果您希望9999 成为可能的结果,请改用.Next(1000, 10000)。

标签: c# linq loops syntax random


【解决方案1】:

如果我理解正确,您只需要一个已知范围内的数字,并且该数字不应该已经在字典中,所以不要Random:

Enumerable.Range(1000, 9999)
          .Where(n => !dict.ContainsKey(n))
          .FirstOrDefault();

【讨论】:

  • @Neurodefekt 这总是给出不在dict 中的最低(四位数或小于10999)数字。
  • 你甚至可以说.FirstOrDefault(n => !dict.ContainsKey(n))。在任何情况下,如果 dict 包含 1000 到 10998 之间的所有键,则返回 0。哎呀,您是否意识到 Range(1000, 9999) 给出了从 1000 到 10998 的数字?
  • @sll:让我更明确地说:(new Random()).Next(1000, 9999) 对应于Enumerable.Range(1000, 8999) 的范围,所以你的答案弄错了。如果原始海报的真正含义是(new Random()).Next(1000, 10000),那就是Enumerable.Range(1000, 9000)。
【解决方案2】:

您需要一种方法来为流式随机数生成无限可枚举。您可以使用Enumerable.Range(0, int.MaxValue) 进行近似:

var rand = new Random();
var r = Enumerable.Range(0, int.MaxValue)
    .Select(s => rand.Next(1000, 9999))
    .SkipWhile(s => md_StreamDict.ContainsKey(s.ToString()))
    .First();

那么r 将包含字典中未包含的新键值。

【讨论】:

  • 这不会创建一个无限列表,而且您不能 100% 确定它会包含 1000-9999 之间的所有值。您必须要么做 while(true) yield random.Next() 要么在该范围内创建 int 列表并随机播放。
  • @JonB:没错。虽然我认为如果你一直到4294967296 没有找到丢失的密钥,你可以写一本关于统计异常的书。更不用说如果你的字典那么满了,你会等待很长时间才能找到任何方法的一个洞。
  • 不要在循环中创建Random 的实例!见stackoverflow.com/a/3053811/880990
  • @OlivierJacot-Descombes:说得好。更新。我只是盲目地以 LINQ 格式复制 OP 的原始逻辑。
【解决方案3】:

这里还有一些香蕉:

您可以创建一个 IEnumerable 来生成随机数,例如:

public class RandomList : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        Random r = new Random();

        while (true)
            yield return r.Next(1000, 9999);
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

那么你可以这样做:

// I guess one could implement a seed constructor, if one was so inclined
RandomList rl = new RandomList(); 

int vStreamID = rl.SkipWhile(i => md_StreamDict.ContainsKey(i)).First();

【讨论】:

    【解决方案4】:

    现在我已将您的问题读了 3 遍,但我仍然不知道为什么(或如何)有人会使用 LINQ 表达式来做到这一点。我看到的唯一较短的解决方案是我在评论中写的:

    var r = new Random();
    
    do {
        vStreamID = r.Next(1000, 9999).ToString();
    } while (md_StreamDict.ContainsKey(vStreamID));
    

    【讨论】:

      【解决方案5】:

      一种方法是使用 yield 来返回值

       private static Random rand = new Random();
      
          private static void Test()
          {
              Dictionary<string, string> values = new Dictionary<string, string>();
              string vStreamId = GetNewValues().Where(x => !values.ContainsKey(x)).First();
          }
      
          public static IEnumerable<string> GetNewValues()
          {
              While(true)
              {
                 yield return rand.Next(1000, 9999).ToString();
              }
          }
      

      请注意,如果您在每个循环中生成一个新的 Random,则每次都将获得相同的值,因为它基于系统时钟。

      【讨论】:

      • 这将产生一个值。
      • 不确定我是否理解 Jon,GetNewValues() 函数在每次调用时都会返回一个新值,并且 LINQ 语句将选择第一个不在字典中的值?
      • 尝试运行它。获取新值将产生一个随机数,然后返回。你必须做while(true) yield return...
      猜你喜欢
      • 2011-10-15
      • 1970-01-01
      • 2013-09-25
      • 2015-03-22
      • 2013-02-17
      • 2010-10-07
      • 2010-11-11
      • 1970-01-01
      • 2013-12-21
      相关资源
      最近更新 更多