简体   繁体   English

Python 递归与迭代

[英]Python Recursion vs Iteration

I can't find the error in my code.我在我的代码中找不到错误。 It runs just fine on my cmd but won't pass the lab platform tests.它在我的 cmd 上运行良好,但无法通过实验室平台测试。 Here is the question:这是问题:

You need to design an iterative and a recursive function called replicate_iter and replicate_recur respectively which will receive two arguments: times which is the number of times to repeat and data which is the number or string to be repeated.您需要分别设计一个名为replicate_iter 和replicate_recur 的迭代和递归函数,它们将接收两个参数:times 是要重复的次数,而data 是要重复的数字或字符串。

The function should return an array containing repetitions of the data argument.该函数应返回一个包含重复数据参数的数组。 For instance, replicate_recur(3, 5) or replicate_iter(3,5) should return [5,5,5] .例如, replicate_recur(3, 5)replicate_iter(3,5)应该返回[5,5,5] If the times argument is negative or zero, return an empty array.如果时间参数为负数或零,则返回一个空数组。 If the argument is invalid, raise a ValueError ."如果参数无效,则引发ValueError 。”

Now here is my code:现在这是我的代码:

my_list1 = []
my_list2 = []
   def replicate_iter(times,data):
      try:
        if type(data) != int and type(data) != str:
          raise ValueError('Invalid Argument')
        times += 0
          if times > 0:
            x = 0
            while x < times:
            my_list2.append(data)
            x = x+1
           return my_list2
         else:
          return []
    except (ValueError,AttributeError,TypeError):
        raise ValueError('Invalid Argument')

def replicate_recur(times,data):
    try:
        if type(data) != int and type(data) != str:
          raise ValueError('Invalid Argument')
        times += 0
        if times <= 0:
          return my_list1
        else:
          my_list1.append(data)
          return replicate_recur(times-1,data)
    except(AttributeError,TypeError):
        raise ValueError('Invalid Argument')

This works!这有效!

def replicate_iter(times, data):
    if((not isinstance(times, int)) or (not isinstance(data, (int, float, long, complex, str)))):
            raise ValueError("Invalid arguments")
    elif(times <= 0):
            return []
    else:
            array = []
            for x in range(times):
                    array.append(data)
            return array


def replicate_recur(times, data):
    if((not isinstance(times, int)) or (not isinstance(data, (int, float, long, complex, str)))):
            raise ValueError("Invalid arguments")
    elif(times <= 0):
            return []
    else:
            return ([data] + replicate_recur((times - 1), data))        
def replicate(c,times):
    return [c]*times

def replicate2(c,times):
    return [c for i in range(times)]

def replicate3(c,times):
    result = []
    for i in range(times): result.append(c)
    return result

def replicate4(c,times):
    return [c] + (replicate4(c,times-1) if times > 0 else [])

Basic implementation for both (although they are not good Python to be honest) would be:两者的基本实现(尽管老实说它们不是好的 Python)将是:

def replicate_iter(times, data):
    result = []
    for _ in range(times):  # xrange in Python2
        result.append(data)
    return result


def replicate_recur(times, data):
    if times <= 0:
        return []
    return [data] + replicate_recur(times - 1, data)


assert replicate_iter(3, 5) == [5, 5, 5]
assert replicate_recur(3, 5) == [5, 5, 5]
assert replicate_iter(4, "abc") == ["abc", "abc", "abc", "abc"]
assert replicate_recur(4, "abc") == ["abc", "abc", "abc", "abc"]

They are simple, straightforward and shows basic differences between both approaches.它们简单、直接,并显示了两种方法之间的基本差异。

Pythonic code would either use multiplying a sequence or list comprehension: Pythonic 代码将使用乘法序列或列表理解:

result = [data for _ in range(times)]
result = [data] * times

Recursion wouldn't really be used for this task in any production code.在任何生产代码中,递归都不会真正用于此任务。

I think your code is closer to being correct than other folks are saying -- you just need to rearrange some elements:我认为你的代码比其他人说的更接近正确——你只需要重新排列一些元素:

def replicate_iter(times, data):
    if type(data) != int and type(data) != str:
        raise TypeError('Invalid Argument')

    try:
        my_list = []

        if times > 0:
            for _ in range(times):
                my_list.append(data)

        return my_list

    except (ValueError, TypeError):
        raise ValueError('Invalid Argument') from None

def replicate_recur(times, data):
    if type(data) != int and type(data) != str:
        raise TypeError('Invalid Argument')

    try:
        my_list = []

        if times > 0:
            my_list.append(data)
            my_list.extend(replicate_recur(times - 1, data))

        return my_list

    except (ValueError, TypeError):
        raise ValueError('Invalid Argument') from None

I agree the error handling is a bit complicated but that's what the specification called for.我同意错误处理有点复杂,但这正是规范所要求的。 Though specific about the times argument, the specification was ambiguous about which error to generate if the data isn't one of the accepted types, but TypeError seems to make sense.尽管特定于times参数,但如果data不是可接受的类型之一,规范对于生成哪个错误是模棱两可的,但 TypeError 似乎是有道理的。

for the recursive version I recommend tail recursion as you are already doing, the other examples even if simplest use list concatenation ( + ) which produce a new list with a copy of the elements of each, that is too much waste of time and space when you can pass around a single list where you add elements to it as you do with a iterative version对于递归版本,我推荐尾递归,就像你已经在做的那样,其他例子即使最简单地使用列表连接( + ),它会产生一个带有每个元素副本的新列表,这太浪费时间和空间了您可以传递一个列表,您可以像使用迭代版本一样向其中添加元素

here is a example这是一个例子

def replicate_recur(times, data, result=None):
    if result is None:
        result = []
    if times <= 0 :
        return result
    else:
        result.append(data)
        return replicate_recur(times-1,data,result)

Additionally, you can avoid repeat the same check over and over again by using an auxiliary function此外,您可以通过使用辅助功能避免一遍又一遍地重复相同的检查

def replicate_recur_aux(times, data, result):
    if times <= 0 :
        return result
    else:
        result.append(data)
        return replicate_recur_aux(times-1,data,result)  

def replicate_recur(times, data):
    if not isinstance(data, int) and not isinstance(data, str):
        raise ValueError('Invalid Argument')
    return replicate_recur_aux(times,data,[])

this way the main function do all the check that needs to be done and all the necessary setup just once so the auxiliary function do its job without any worry or unnecessary checks of the same thing.这样,主要功能只需一次完成所有需要完成的检查和所有必要的设置,以便辅助功能完成其工作,而无需担心或不必要地检查相同的事情。

For the iterative version, any of the other examples are good, but maybe your teacher want something like this对于迭代版本,任何其他示例都很好,但也许你的老师想要这样的东西

def replicate_iter(times, data):
    if not isinstance(data, int) and not isinstance(data, str):
        raise ValueError('Invalid Argument')
    result = []
    if times <=0:
        return result
    for i in range(times):
        result.append(data)
    return result

now notice the similarity between both version, you can easy transform one into the other现在注意两个版本之间的相似性,您可以轻松地将一个转换为另一个

I think the iterative version is straightforward, and others have provided good answers.我认为迭代版本很简单,其他人已经提供了很好的答案。 As for the recursion, I always like to see if I can come up with a divide and conquer strategy rather than whittle the problem down one step at a time, to avoid blowing out the stack.至于递归,我总是喜欢看看我是否可以提出一个分而治之的策略,而不是一步一步地将问题缩小,以避免堆栈溢出。 The following does that:以下是这样做的:

def replicate_recur(times, data):
    def f(n, value):
        if n < 1:
            return [] 
        result = f(n // 2, value) * 2
        if n % 2 > 0:
            result += [value] 
        return result

    if not isinstance(data, int) and not isinstance(data, str):
        raise ValueError('Must be int or str')
    return f(times, data)

This uses an internal recursive function f() to avoid repeating the type checking for data .这使用内部递归函数f()来避免重复data的类型检查。 The recursion constructs a list of half the size, and either doubles it (if the current n is even) or doubles it and adds one more occurrence of data (if the current n is odd).递归构造一个大小减半的列表,然后将其加倍(如果当前n为偶数)或将其加倍并再添加一次data (如果当前n为奇数)。

Try it, you'll like it!试试吧,你会喜欢的!

I tried this and it worked for that test!我试过这个,它适用于那个测试!

def replicate_iter(times,data):
    Array = []
    if type(times) != int or not isinstance(data, (int, float, complex, str)):
        raise ValueError ('Invalid Value')
    elif times < 0:
        return []
    else:
        for x in range (times):
            Array.append(data)
        return Array


def replicate_recur(times,data):
    Array = []
    if type(times) != int or not isinstance(data, (int, float, complex, str)):
        raise ValueError ('Invalid Value')
    elif times <= 0:
        return []
    else:
       Array = replicate_recur(times-1,data)
       Array.append(data)
    return Array
def replicate_iter(times, data):
    if not isinstance(data, int) and not isinstance(data, str):
        raise ValueError('Must be int or str')
    if times <= 0:
        return []
    return [data for _ in range(times)]

def replicate_recur(times, data):
    if not isinstance(data, int) and not isinstance(data, str):
        raise ValueError('Must be int or str')
    if times <= 0:
        return []
    return [data] + replicate_recur(times-1, data)

Output输出

>>> replicate_iter(3, 5)
[5, 5, 5]

>>> replicate_recur(3, 5)
[5, 5, 5]

Edge cases边缘情况

>>> replicate_iter(0, 5)
[]

>>> replicate_iter(3, list())
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    replicate_iter(3, list())
  File "C:/temp/temp.py", line 3, in replicate_iter
    raise ValueError('Must be int or str')
ValueError: Must be int or str

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM