【问题标题】:How do I write this in Ruby/Python? Or, can you translate my LINQ to Ruby/Python?我如何用 Ruby/Python 编写这个?或者,你能把我的 LINQ 翻译成 Ruby/Python 吗?
【发布时间】:2010-09-12 10:19:36
【问题描述】:

昨天,我问了this 问题,但从未真正得到我真正满意的答案。我真的很想知道如何使用诸如 Ruby 之类的函数式语言生成一个包含 N 个唯一随机数的列表,而不必在样式上非常强制。

由于我没有看到任何我真正喜欢的东西,我已经编写了我在 LINQ 中寻找的解决方案:


       static void Main(string[] args)
        {
            var temp = from q in GetRandomNumbers(100).Distinct().Take(5) select q;
        }

        private static IEnumerable GetRandomNumbers(int max)
        {
            Random r = new Random();
            while (true)
            {
                yield return r.Next(max);
            }
        }

你能把我的 LINQ 翻译成 Ruby 吗? Python?还有其他函数式编程语言吗?

注意:请尽量不要使用太多的循环和条件 - 否则解决方案是微不足道的。另外,我宁愿看到一个解决方案,您不必生成比 N 大得多的数组,这样您就可以删除重复项并将其修剪为 N。

我知道我很挑剔,但我真的很想看到一些优雅的解决方案来解决这个问题。 谢谢!

编辑:
为什么所有的反对票?

最初我的代码示例在 Take() 之后有 Distinct(),正如许多人指出的那样,这可能会给我留下一个空列表。我已经更改了调用这些方法的顺序,以反映我最初的意思。

道歉:
我被告知这篇文章相当势利。我并不是要暗示 LINQ 比 Ruby/Python 更好。或者我的解决方案比其他人的解决方案要好得多。我的目的只是学习如何在 Ruby 中做到这一点(有一定的限制)。如果我被认为是个混蛋,我很抱歉。

【问题讨论】:

  • 我们很清楚这一点:尽管 Python 有一些函数式结构,比如列表推导式,但它确实不是一种函数式语言,这不是一个你可以在真正的函数式中轻松解决的问题Python 中的样式。
  • 我没有得到要求。是取 N 个值并在该集合中定位不同的值吗?还是要找到一组具有 N 个不同值的大小?
  • 如果你取(5) 然后不同......你可能会得到 1 个数字。
  • @David:是的,我只是改变了这些顺序。谢谢!
  • 我敢打赌,这意味着 LINQ 的优势如此明显,以至于没有人可以生产出符合您崇高的“优雅”标准的 Ruby 或 Python。只是猜测。这个问题很蹩脚,但不是那个蹩脚的。

标签: python ruby linq functional-programming


【解决方案1】:
>>> import random
>>> print random.sample(xrange(100), 5)
[61, 54, 91, 72, 85]

这应该在0 — 99 范围内产生 5 个唯一值。 xrange 对象根据请求生成值,因此没有内存用于未采样的值。

【讨论】:

  • xrange() 实际上并不使用生成器。这是一个假名单。生成器不是序列,不能被索引,所以如果它是一个, random.sample() 会失败。
  • 为什么样本是独一无二的?似乎没有任何类型的过滤器来确保唯一性。
  • random.sample() 函数自己完成。 “返回从种群序列中选择的唯一元素的 k 长度列表”。
【解决方案2】:

在 Ruby 中:

a = (0..100).entries.sort_by {rand}.slice! 0, 5

更新:这是一种稍微不同的方式: a = (0...100).entries.sort_by{rand}[0...5]

编辑:

在 Ruby 1.9 中,您可以这样做:

Array(0..100).sample(5) 

【讨论】:

    【解决方案3】:

    嗯...怎么样(Python):

    s = set()
    while len(s) <= N: s.update((random.random(),))
    

    【讨论】:

      【解决方案4】:

      我将放弃使用“随机”模块的最简单解决方案,因为我认为这并不是你真正想要的。以下是我认为您在 Python 中寻找的内容:

      >>> import random
      >>> 
      >>> def getUniqueRandomNumbers(num, highest):
      ...     seen = set()
      ...     while len(seen) < num:
      ...         i = random.randrange(0, highest)
      ...         if i not in seen:
      ...             seen.add(i)  
      ...             yield i
      ... 
      >>>
      

      向您展示它是如何工作的:

      >>> list(getUniqueRandomNumbers(10, 100))
      [81, 57, 98, 47, 93, 31, 29, 24, 97, 10]
      

      【讨论】:

        【解决方案5】:

        这是另一个 Ruby 解决方案:

        a = (1..5).collect { rand(100) }
        a & a
        

        我认为,使用您的 LINQ 语句, Distinct 将在 5 个已被占用后删除重复项,因此您不能保证获得 5 个返回。不过,如果我错了,有人可以纠正我。

        【讨论】:

        • 是的,我也担心这个。但是,我还不能生成一个少于 5 个元素的数组。然而,这并不意味着它可能发生。不过,最坏的情况是,我可以在 Take() 之前调用 Distinct(),对吗?
        【解决方案6】:

        编辑:好吧,只是为了好玩,一个更短更快的(并且仍在使用迭代器)。

        def getRandomNumbers(max, size) :
            pool = set()
            return ((lambda x :  pool.add(x) or x)(random.randrange(max)) for x in xrange(size) if len(a) < size)
        
        print [x for x in gen(100, 5)]
        [0, 10, 19, 51, 18]
        

        是的,我知道,单行应该留给 perl 爱好者,但我认为这个很强大,不是吗?

        这里是旧消息:

        天哪,这一切多么复杂!让我们成为pythonic:

        import random
        def getRandomNumber(max, size, min=0) :
           # using () and xrange = using iterators
           return (random.randrange(min, max) for x in xrange(size))
        
        print set(getRandomNumber(100, 5)) # set() removes duplicates
        set([88, 99, 29, 70, 23])
        

        享受

        编辑:正如评论员所注意到的,这是问题代码的精确翻译。

        为了避免我们在生成列表后删除重复的问题,导致数据太少,您可以选择另一种方式:

        def getRandomNumbers(max, size) :
            pool = []
            while len(pool) < size :
                tmp = random.randrange(max)
                if tmp not in pool :
                    yield pool.append(tmp) or tmp
        
        print [x for x in getRandomNumbers(5, 5)]
        [2, 1, 0, 3, 4]
        

        【讨论】:

        • 如果您删除了重复项,您最终会得到比预期更少的值吗?
        • 是的,但他在他的问题中做了同样的事情,所以这是一个精确的翻译。这就是我们所要求的:翻译。
        • 他没有 - .Take(5) 发生在 .Distinct 调用之后,因此将从已经不完整的序列中提取 5 个项目。
        • 没关系 - 只需阅读有关问题的评论,他修复了 .Distinct / .Take 的顺序
        【解决方案7】:

        在 Ruby 1.9 中:

        Array(0..100).sample(5)
        

        【讨论】:

          【解决方案8】:

          带有数字 Python 的 Python:

          from numpy import *
          a = random.random_integers(0, 100, 5)
          b = unique(a)
          

          瞧!当然你可以用函数式编程风格做类似的事情,但是......为什么?

          【讨论】:

          • 因为它不使用迭代器并将所有整数存储在内存中。
          【解决方案9】:
          import random
          
          def makeRand(n):
             rand = random.Random()
             while 1:
                yield rand.randint(0,n)
             yield rand.randint(0,n)      
          
          gen = makeRand(100)      
          terms = [ gen.next() for n in range(5) ]
          
          print "raw list"
          print terms
          print "de-duped list"
          print list(set(terms))
          
          # produces output similar to this
          #
          # raw list
          # [22, 11, 35, 55, 1]
          # de-duped list
          # [35, 11, 1, 22, 55]
          

          【讨论】:

            【解决方案10】:

            好吧,首先你用 Python 重写 LINQ。那么你的解决方案是单行的:)

            from random import randrange
            
            def Distinct(items):
                set = {}
                for i in items:
                    if not set.has_key(i):
                        yield i
                        set[i] = 1
            
            def Take(num, items):
                for i in items:
                    if num > 0:
                        yield i
                        num = num - 1
                    else:
                        break
            
            def ToArray(items):
                return [i for i in items]
            
            def GetRandomNumbers(max):
                while 1:
                    yield randrange(max)
            
            print ToArray(Take(5, Distinct(GetRandomNumbers(100))))
            

            如果你把上面所有的简单方法都放在一个名为 LINQ.py 的模块中,你可以给你的朋友留下深刻印象。

            (免责声明:当然,这并不是实际上在 Python 中重写 LINQ。人们误解为 LINQ 只是一堆琐碎的扩展方法和一些新语法。LINQ 真正高级的部分但是,它是自动生成 SQL,因此当您查询数据库时,它是实现 Distinct() 的数据库,而不是客户端。)

            【讨论】:

            • 不错。只是一点评论:您应该在 Distence 中使用 set() 而不是哈希,而 ToArray 可以使用 list()。
            【解决方案11】:

            这是从您的解决方案到 Python 的音译。

            首先,一个生成随机数的生成器。这不是很 Pythonic,但它与您的示例代码非常匹配。

            >>> import random
            >>> def getRandomNumbers( max ):
            ...     while True:
            ...             yield random.randrange(0,max)
            

            这是一个收集一组 5 个不同值的客户端循环。这 - 再次 - 不是最 Pythonic 的实现。

            >>> distinctSet= set()
            >>> for r in getRandomNumbers( 100 ):
            ...     distinctSet.add( r )
            ...     if len(distinctSet) == 5: 
            ...             break
            ... 
            >>> distinctSet
            set([81, 66, 28, 53, 46])
            

            目前尚不清楚为什么要使用随机数生成器 - 这是少数几件如此简单以至于生成器无法简化它的事情之一。

            更 Pythonic 的版本可能类似于:

            distinctSet= set()
            while len(distinctSet) != 5:
                distinctSet.add( random.randrange(0,100) )
            

            如果要求是生成 5 个值并在这 5 个中找到不同的值,那么类似于

            distinctSet= set( [random.randrange(0,100) for i in range(5) ] )
            

            【讨论】:

              【解决方案12】:

              也许这会满足您的需求,并且看起来更 linqish:

              from numpy import random,unique
              
              def GetRandomNumbers(total=5):
                  while True:
                      yield unique(random.random(total*2))[:total]
              
              randomGenerator = GetRandomNumbers()
              
              myRandomNumbers = randomGenerator.next()
              

              【讨论】:

                【解决方案13】:

                这是另一个 python 版本,更接近于 C# 代码的结构。没有提供不同结果的内置函数,所以我添加了一个函数来执行此操作。

                import itertools, random
                
                def distinct(seq):
                    seen=set()
                    for item in seq:
                        if item not in seen:
                            seen.add(item)
                            yield item
                
                def getRandomNumbers(max):
                    while 1:
                        yield random.randint(0,max)
                
                for item in itertools.islice(distinct(getRandomNumbers(100)), 5):
                    print item
                

                【讨论】:

                  【解决方案14】:

                  我无法真正阅读您的 LINQ,但我认为您正在尝试获取 5 个最多 100 的随机数,然后删除重复项。

                  这是一个解决方案:

                  def random(max)
                      (rand * max).to_i
                  end
                  
                  # Get 5 random numbers between 0 and 100
                  a = (1..5).inject([]){|acc,i| acc << random( 100)}
                  # Remove Duplicates
                  a = a & a
                  

                  但也许您实际上是在寻找 0 到 100 之间的 5 个不同的随机数。在这种情况下:

                  def random(max)
                      (rand * max).to_i
                  end
                  
                  a = []
                  while( a.size < 5)
                      a << random( 100)
                      a = a & a
                  end
                  

                  现在,这个可能会违反您“没有太多循环”的感觉,但大概 Take 和 Distinct 只是对您隐藏了循环。只需向 Enumerable 添加方法以隐藏 while 循环就很容易了。

                  【讨论】:

                  • True:我意识到 Take & Distinct 可能在幕后循环。我的意思是没有你必须写的循环......不过,我确实喜欢你的第二个解决方案。谢谢!
                  • (rand * max).to_i 应该写成rand max
                  猜你喜欢
                  • 2011-07-27
                  • 2020-09-19
                  • 2010-10-10
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-05-20
                  • 1970-01-01
                  • 2015-10-31
                  • 2013-01-16
                  相关资源
                  最近更新 更多