【问题标题】:Most minimal way to repeat things in python在 python 中重复事情的最简单的方法
【发布时间】:2014-09-21 13:04:43
【问题描述】:

假设您要编写一个程序,要求用户输入 X 个数字并将它们存储在 A 中,然后输入 Y 个数字并将它们存储在 B 中

在你投票结束之前:是的,这个问题已经被问到了herehereherehere 以及可能的其他地方,但我在这个问题中回复了每个建议的解决方案,解释了为什么它们'不是我要找的东西,所以如果你认为它是重复的,请在投票结束之前继续阅读。这是一个严肃的问题,请参阅最后一段,了解支持我在此处尝试实现的功能的一小部分语言。

A = []
B = []

# First possibilty : using while loops
# you need to have a counter
i = 0
while (i < X):
    A.append(input())
    # and increment it yourself
    i+=1
# you need to reset it
i = 0
while (i < Y):
    B.append(input())
    # and increment it again
    i+=1

所以基本上你需要重复某件事x次,然后再重复Y次,但是python只有while循环和for循环。 while 循环不是重复某件事 N 次,而是在某个条件为 True 时重复某件事,这就是为什么您必须使用计数器、初始化它、递增它并对其进行测试的原因。

第二种解决方案是使用 for 循环:

# No need to create a counter
for x in xrange(x):
    A.append(input())
    # No need to increment the counter

# no need to reset the counter
for x in xrange(Y):
    B.append(input())

这好多了 :),但还是有一些令人讨厌的地方:当我不需要 x 时,为什么我还要提供“x”?在我的代码中,我不需要知道我在哪个循环迭代中,我只需要重复 N 次,那么为什么我必须创建一个计数器呢?没有办法完全摆脱柜台吗?想象一下你可以写这样的东西:

# No need to supply any variable !
repeat(x):
    A.append(input())

repeat(Y):
    B.append(input())

这样不是更优雅吗?这不会更准确地反映您对代码的潜在读者的意图吗?不幸的是,python 不够灵活,无法创建新的语言结构(更高级的语言允许这样做)。所以我仍然必须使用 for 或 while 来循环事物。考虑到这一限制,我如何尽可能接近上层代码?

这是我尝试过的

def repeat(limit):
    if hasattr(repeat,"counter"):
        repeat.counter += 1
        return repeat.counter < limit
    repeat.limit   = limit
    repeat.counters = 0
    return limit > 0 and True

while repeat(x):
    A.append(input())

while repeat(Y):
    B.append(input())

此解决方案适用于简单循环,但显然不适用于嵌套循环。

我的问题是

你知道任何其他可能的实现,可以让我获得与重复相同的接口吗?

反驳建议的解决方案

建议的解决方案 1.1:使用 itertools.repeat

您必须将您的代码放在一个函数中,并以某种方式在 itertools.repeat 中调用该函数。它与上面的重复实现不同,您可以在任意缩进的代码块上使用它。

建议解决方案 1.2:在 for 循环中使用 itertools.repeat

import itertools

for _ in itertools.repeat(None, N):
    do_something()

你还在写一个for循环,而for循环需要一个变量(你提供了_),这完全没有抓住重点,再看一遍:

while repeat(5):
    do_x()
    do_y()
    do_z()

没有提供任何东西只是为了循环。这就是我想要实现的目标。

建议的解决方案 2:使用 itertools.count

1) 你已经成功了一半。是的,您无需手动增加计数器,但仍需要手动创建计数器。示例:

c = itertools.count(1,1) # You still need this line
while( c.next() < 7):
    a = 1
    b = 4
    d = 59
    print "haha"
    # No need to increment anything, that's good.
    # c.incr() or whatever that would be

您还需要将计数器重置为 0,如果您在其上使用 with 语句,我认为这可以隐藏在函数中,以便在 with 语句的每一端,计数器都会被重置。

建议的解决方案 3:使用 with 语句

我从未使用过它,但据我所见,它用于在另一个函数中扫描 try/catch 代码。你怎么能用它来重复事情呢?我很高兴学习如何使用我的第一个 with 语句 :)

建议的解决方案 4:使用迭代器/生成器、xrange、列表推导...

不,不,不……

如果你写: [X() for _ in xrange(4)]

你是: 1)为您不关心的值创建一个列表。 2) 提供一个计数器 (_)

如果你写:

for _ in xrange(4):
   X()

见上文第 2) 点

如果你写:

def repeat(n):
    i = 0
    while i < n : 
        yield i < n
        i+1

那你应该如何使用 repeat ?像这样 ? :

for x in repeat(5):
    X() 

然后参见上面的第 2) 点。另外,这基本上是 xrange,所以为什么不使用 xrange 来代替。

还有其他很酷的想法吗?

有些人问我来自什么语言,因为他们不熟悉这种 repeat(N) 语法。我的主要编程语言是 python,但我确信我已经在其他语言中看到过这种语法。访问我的书签和在线搜索表明以下语言具有这种很酷的语法:

Ruby

它只是称为时间而不是重复。

#!/usr/bin/env ruby

3.times do
  puts "This will be printed 3 times"
end

print "Enter a number: "
num = gets.chomp.to_i

num.times do
  puts "Ruby is great!"
end

Netlogo

 pd repeat 36 [ fd 1 rt 10 ]
 ;; the turtle draws a circle

AppleScript

[applescript]
repeat 3 times
--commands to repeat
end repeat
[/applescript]

Verilog

  1 module repeat_example();
  2 reg  [3:0] opcode;
  3 reg  [15:0] data;
  4 reg        temp;
  5 
  6 always @ (opcode or data)
  7 begin
  8   if (opcode == 10) begin
  9     // Perform rotate
 10     repeat (8) begin 
 11        #1  temp = data[15];
 12       data = data << 1;
 13       data[0] = temp;   
 14     end 
 15   end
 16 end
 17 // Simple test code
 18 initial begin
 19    $display (" TEMP  DATA");
 20    $monitor (" %b     %b ",temp, data);
 21     #1  data = 18'hF0;
 22     #1  opcode = 10;
 23     #10  opcode = 0;
 24     #1  $finish;
 25 end
 26 
 27 endmodule

Rebol

repeat count 50 [print rejoin ["This is loop #: " count]]

Forth

LABEL 10 0 DO something LOOP 将重复某事 10 次,您不必提供计数器(Forth 自动将其存储在特殊变量 I 中)。

   : TEST   10 0 DO  CR ." Hello "  LOOP ;

Groovy

您可以编写 1.upTo(4){ code } 或 4.times{ code } 来执行代码 4 次。 Groovy 在循环方面非常出色,它有五六种方法(upTo、times、step、java for、groovy for、foreach 和 while)。

// Using int.upto(max).
0.upto(4, createResult)
assert '01234' == result

// Using int.times.
5.times(createResult)
assert '01234' == result

// Using int.step(to, increment).
0.step 5, 1, createResult
assert '01234' == result

【问题讨论】:

  • 您尝试这样做有什么特别的原因吗?
  • 如果你想要速度,用 C 来做,但请注意,你已经花更多的时间思考它而不是运行它。如果您想要优雅,请使用函数式语言。如果您想快速实用地做到这一点,那么您已经有了答案。在 Python 中。
  • -1。我真的看不出这个问题对除了你之外的任何人有什么价值,因为你基本上是在问“我怎样才能以一种满足我对‘优雅’的高度主观和任意定义的方式编写这段代码”
  • 如果不改变 python 的循环结构,就永远不会有一种合理的方式来做你想做的事,至少不会以一种不会最终成为 1) 显着更多代码的方式,打败你的主要目标和2)明显不太清楚,再次打败你的目的。 python中的循环是围绕迭代的概念设计的。而且由于 python 的核心习惯之一是应该有一种且只有一种明显的做某事的方式,所以当你不关心迭代器的返回时,它们拒绝进行循环。
  • 使用_ 表示您不使用迭代变量,因为约定已决定。您需要“不创建计数器”是愚蠢的,当然总会有一个 some 级别的计数器。 Python 语法不适合隐藏它。

标签: python loops nested-loops repeat


【解决方案1】:

警告:不要在生产环境中这样做!

我强烈建议您使用已提供的众多列表理解解决方案之一,因为这些解决方案非常简单。但是,如果您喜欢冒险并且真的,真的,真的想要这样做,请继续阅读......

我们可以稍微调整您的 repeat 解决方案,以便嵌套工作,方法是存储根据调用位置而变化的状态。我们如何做到这一点?当然是通过检查堆栈!

import inspect

state = {}

def repeat(limit):
    s = tuple((frame, line) for frame, _, line, _, _, _ in inspect.stack()[1:])
    counter = state.setdefault(s, 0)
    if counter < limit:
        state[s] += 1
        return True
    else:
        del state[s]
        return False

我们设置了一个状态 dict 来跟踪调用 repeat 函数的每个唯一位置的当前计数器,方法是关闭 (stack_frame, line_number) 元组的元组。这与您原来的 repeat 函数有相同的警告,因为 break 确实破坏了一切,但在大多数情况下似乎有效。示例:

while repeat(2):
    print("foo")
    while repeat(3):
        print("bar")

输出:

foo
bar
bar
bar
foo
bar
bar
bar

【讨论】:

  • 我正在考虑这样做,但我的“优雅”指标有正确性和实用性的基础,所以这是行不通的,因为它不能处理多个线程访问同一模块的可能性。为了完整性,也许可以添加一些线程局部变量,但请认真不要使用它。无论如何 +1。
  • 我就是这么想的,我心想,没有办法让 repeat 知道它是从同一个 while 循环还是从另一个循环调用的,除非你为它提供另一个可选参数(例如深度),它会检查它。但是这个接口很糟糕,为什么会有人关心给出第二个参数,它应该是函数的工作来解决它。
  • 另一种可能性是让函数重复知道它是从哪里调用的,即使用检查工具。但我不知道如何做到这一点,我不确定它的效率/优雅性。
  • @ychaouche 不,您要求的不是优雅,而是语法糖。要么使用该语言的最佳实践,要么使用其他语言,或者更好,编写自己的(或 fork Python 并使用解释器为您提供 repeat 关键字,它完全符合您的要求,但可能没有人会用你的叉子)。
  • repeat(2) 的输出中真的有四个 foo 吗?
【解决方案2】:

你肯定是想多了。如果我正确理解了您的(相当冗长的)问题,您希望避免重复代码。在这种情况下,您的第一个调用端口应该是编写一个函数。这看起来相对简单:您需要的是一个函数,它接受一个参数x(我使用n 只是因为它让我想起了整数)并返回一个包含这么多输入元素的列表。类似(未经测试):

def read_inputs(n):
    ret_val = []
    for i in range(n):
        ret_val.append(input())
    return retval

您的代码的其余部分很简单:

A = read_inputs(X)
B = read_inputs(Y)

【讨论】:

  • +1 简单、惯用、易读。尽管在这些迭代的日子里,您可以将正文减少到 for _ in range(n): yield input() 并让调用者决定应该如何存储输出。
  • @msw 那不要求调用者也使用循环吗?还是A = list(read_input(X)) 工作?
  • read_input() 已经返回了一个列表,对它应用list() 函数有什么价值?循环在函数内部,因此每次调用都会重复。
  • @holdenweb,对于@msw 的代码,在我看来,yield 的存在表明该函数将返回一个生成器,因此我的list 问题。
  • 同意。当我写这篇文章时,我忘记了 OP 特殊欲望的所有细节;但是是的,list(read_inputs(n)) 会起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-08
  • 2014-02-14
  • 2018-09-27
  • 2013-04-22
  • 2019-03-24
  • 1970-01-01
  • 2017-01-31
相关资源
最近更新 更多