我如何用 Ruby/Python 编写这个?或者,你能把我的 LINQ 翻译成 Ruby/Python 吗?

Posted

技术标签:

【中文标题】我如何用 Ruby/Python 编写这个?或者,你能把我的 LINQ 翻译成 Ruby/Python 吗?【英文标题】:How do I write this in Ruby/Python? Or, can you translate my LINQ to 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。只是猜测。这个问题很蹩脚,但不是那个蹩脚的。 【参考方案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_byrand[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

以上是关于我如何用 Ruby/Python 编写这个?或者,你能把我的 LINQ 翻译成 Ruby/Python 吗?的主要内容,如果未能解决你的问题,请参考以下文章

“”和“”有啥区别,我如何用字符来测试前者?

我如何为这个问题编写 SQL 语句?

我如何用 alamofire 解析 JSON

我如何用 pandas.groupby() 总结时间戳

看我如何用 20 行代码改变女神看我的眼神

我如何用 Jasmine 和 Karma 覆盖承诺响应