【问题标题】:Creating a recursive function to cycle through lists that produce lists that produce lists... and so on创建一个递归函数来循环通过产生列表的列表产生列表......等等
【发布时间】:2020-09-12 19:19:48
【问题描述】:

首先我使用的是 python。

我有一个名为 tier1 的项目列表,看起来像这样。

tier1 = ['a1','a2,'a3',..,'an']

我有 2 个函数,分别称为 functionA 和 functionZ。

它们都将字符串作为参数并产生这样的列表输出。列表必须在执行期间生成,并且从一开始就不可用。只有 tier1 可用。

listOutput = functionA(tier1[0]).

listOutput 看起来像这样

listOutput = ['b1','b2,'b3',..,'bn']

下次在 listOutput 上使用 functionA 让我们说 item 'b1',它将产生

listOutput = functionA('b1')
output:
listOutput = ['bc1','bc2,'bc3',..,'bcn']

这一次使用 functionA 时,让我们说 'bc1',它可能会出现空,因此使用 functionZ 上的 'bc1' 来代替,并将输出存储在某个地方。

listOutput = functionA('bc1')

输出

listOutput = []

所以我用

listOutput = functionZ('bc1')

输出

listOutput = ['series1','series2','series3',....,'seriesn']

现在我必须回去尝试 bc2,直到 bcn 执行相同的逻辑。完成后,我将在“b2”上使用函数 A。等等。

每个项目的深度是可变的。

看起来像这样

只要 listOutput 不为空,就必须对 listOutput 项或 tier1 项使用 functionA,直到它为空。然后 functionZ 必须用于列表中 functionA 为空的任何项目。

在tier1之后,listOutput也将永远是一个列表,也必须一个一个循环,并且必须使用相同的逻辑。

我正在尝试基于此创建一个递归函数,但我被卡住了。

目前为止,

def recursivefunction (idnum): #idnum will be one of the list items from tier1 or the listOutputs produced

    listOutput = functionA(idnum)

    if not listOutput:
        return functionZ(idnum)
    else:
        return recursivefunction(listOutput) 

但是我的函数返回列表,我如何让它们深入到每个列表中,直到使用 functionZ 并且一旦它被用于移动到列表中的下一个项目。

我需要创建一种新的数据结构吗? 我不知道从哪里开始,我应该寻找使用链表创建某种类吗?

【问题讨论】:

  • 我真的不清楚您要做什么。如果您提供带有输入和预期输出的minimal reproducible example,这将非常有帮助
  • 您是说functionA 将单个字符串作为参数tier1[0] 并生成字符串列表作为输出? IE。如果您将functionA 应用于所有tier1(这是一个字符串列表),您最终会得到一个字符串列表列表吗?
  • 每一个都会产生自己的字符串列表是的
  • 对于这种情况,functionA() 会返回空字符串。
  • 顺便说一句,我已经尝试使用 for 循环来执行此操作,但是一旦它越来越深入,我就无法解释每种情况。

标签: python list algorithm recursion data-structures


【解决方案1】:

我理解你的问题的方式:

  • 有一个输入列表tier1,是一个字符串列表
  • 有两个函数,A 和 Z
    • A,当应用于字符串时,返回字符串列表
    • Z,当应用于字符串时,返回一些值(类型不清楚,假设为字符串列表)
  • 算法:
    • 对于tier1的每个元素,将A应用于元素
    • 如果结果是空列表,则将 Z 应用于元素,而不进行进一步处理
    • 否则,如果结果不为空,则对列表应用算法

所以,在 Python 中:

from random import randint
# since you haven't shared what A and Z do, 
# I'm just having them do random stuff that matches your description


def function_a(s):
    # giving it a 75% chance to be empty
    if randint(1, 4) != 1:
        return []
    else:
        # otherwise between 1 and 4 random strings from some selection
        return [['a', 'b', 'c'][randint(0, 2)] for _ in range(randint(1,4))]
        # in the real case, I'm sure the result depends on `s` but it doesn't matter


def function_z(s):
    # otherwise between 0 and 4 random strings from some selection
    return [['x', 'y', 'z'][randint(0, 2)] for _ in range(randint(0,4))]


def solution(xs):
    # this is really the answer to your question:
    rs = []
    for x in xs:
        # first compute A of x
        r = function_a(x)
        # if that's the empty list
        if not r:
            # then we want Z of x instead
            r = function_z(x)
        else:
            # otherwise, it's the same algorithm applied to all of r
            r = solution(r)
        # whatever the result, append it to rs
        rs.append(r)
    return rs


tier1 = ['a1', 'a2', 'a3', 'a4']

print(solution(tier1))

请注意,function_afunction_z 只是根据您指定的结果类型生成随机结果的函数。你没有分享 A 和 Z 的真正逻辑是什么,所以很难验证结果是否是你想要的。

但是,函数 solution 完全按照您所说的去做 - 如果我正确理解您对它的复杂解释的话。

鉴于您的问题的解决方案基本上是这样的:

def solution(xs):
    rs = []
    for x in xs:
        r = function_a(x)
        if not r:
            r = function_z(x)
        else:
            r = solution(r)
        rs.append(r)
    return rs

甚至可以重写为:

def solution_brief(xs):
    return [function_z(r) if not r else solution(r) for r in [function_a(x) for x in xs]]

您应该重新检查您的问题描述。编程的关键是理解问题并将其分解为基本步骤。完成此操作后,代码很快就会跟进。您更喜欢第一种还是第二种解决方案可能取决于经验,也可能取决于微小的性能差异。

顺便说一句,任何写成递归函数的解决方案,也可以写成纯粹的迭代 - 从内存和性能的角度来看,这通常是可取的,但递归函数的优点是非常干净和简单,因此更容易维护.

把我的代码放在嘴边,这是同一问题的迭代解决方案,只是为了好玩(无论如何都不是最佳的):

def solution_iterative(xs):
    if not xs:
        return xs
    rs = xs.copy()
    stack_rs = [rs]
    stack_is = [0]
    while stack_rs:
        r = function_a(stack_rs[-1][stack_is[-1]])
        if not r:
            stack_rs[-1][stack_is[-1]] = function_z(stack_rs[-1][stack_is[-1]])
            stack_is[-1] += 1
        else:
            stack_rs[-1][stack_is[-1]] = r
            stack_rs.append(r)
            stack_is.append(0)
        while stack_is and stack_is[-1] >= len(stack_rs[-1]):
            stack_is.pop()
            stack_rs.pop()
            if stack_is:
                stack_is[-1] += 1
    return rs

【讨论】:

  • 你的回答真的很好,我自己学了编程,所以我不知道从哪里开始解决这个问题。你的建议真的很好。谢谢!
  • 乐于助人 - 理解递归函数的最佳方法是使用好的调试器并单步执行所有代码。请记住,每次递归函数调用自身时,它基本上都会在内存中的某个位置创建函数及其局部变量的新副本,并且它只是从传入的参数开始。大多数新程序员都会遇到麻烦,因为他们要么不t 正确返回值,或者他们假设递归调用就像一个循环,一遍又一遍地对相同的数据进行操作。
  • 这只是一个建议,我认为,但我真的很喜欢 PyCharm。社区版是免费的,对于几乎任何项目来说都足够好,当然对于初学者来说。网上有很多教程可以学习如何使用它。起初它可能看起来有点吓人,但如果你学会使用它,你就会知道如何使用这些工具,如果你成为专业人士或经验丰富的业余爱好者,这些工具也会为你服务。
  • 这可能是一个愚蠢的问题,但函数中是否可以没有 for 循环?因为我的 functionA 和 functionZ 将列表返回给我的格式是这样的testlist = {'cat':[{'id':'b1','year':1995,....}, {'id':'....}...]}。所以我必须这样做'testlist['cat']',当我在'cat' 内的列表中循环时,我必须这样做item['id']
  • 您的意思是您不是在处理列表,而是在处理字典 - 我确信有一个同样简单的解决方案,但您需要分享更多关于您的问题的信息。一般来说,最好(在 StackOverflow 上或在一般编程时)考虑仍然存在实际问题的最简单的可能情况并在该上下文中回答它。你没有给我足够的信息让我知道问题是什么,所以我无法回答你的问题。我不认为“删除 for 循环”是解决方案。
猜你喜欢
  • 2012-01-31
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 2021-04-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多