我如何用 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 吗?的主要内容,如果未能解决你的问题,请参考以下文章