Python 递归地拆分列表,直到平铺

Python 递归地拆分列表,直到平铺,python,Python,我正在写一个激情计划,将决定最好的扑克手给洞卡和社区卡。由于a可以在直线上双向移动,对于给定的5张牌组合,我将其编码为[1,14] 我理解递归,但实现它对我来说是另一回事。我正在寻找一个函数,它可以递归地将所有ACE拆分为所有可能的手动组合,直到所有嵌套列表都用完为止。很明显,这应该适用于最多4个a,忽略了这样一个事实,即在这一点上,你很可能不在乎一个直道 hand = [[1, 14], 2, 3, [1, 14], 7] desired_output = [ [1, 2,

我正在写一个激情计划,将决定最好的扑克手给洞卡和社区卡。由于a可以在直线上双向移动,对于给定的5张牌组合,我将其编码为[1,14]

我理解递归,但实现它对我来说是另一回事。我正在寻找一个函数,它可以递归地将所有ACE拆分为所有可能的手动组合,直到所有嵌套列表都用完为止。很明显,这应该适用于最多4个a,忽略了这样一个事实,即在这一点上,你很可能不在乎一个直道

hand = [[1, 14], 2, 3, [1, 14], 7]

desired_output = [
        [1, 2, 3, 1, 7],
        [1, 2, 3, 14, 7],
        [14, 2, 3, 1, 7],
        [14, 2, 3, 14, 7]
        ]
到目前为止,我并不为我所拥有的感到自豪,特别是因为它返回的是一个列表,而不是像
yield
这样的东西,它将构建我正在寻找的列表:

def split_first_ace(hand):
    aces = [True if isinstance(x, list) else False for x in hand]
    for i, x in enumerate(aces):
        if x:
            ranks_temp = hand.copy()
            ace = ranks_temp.pop(i)
            return [[ace[0]] + ranks_temp, [ace[1]] + ranks_temp]

任何关于解决方案的帮助都将不胜感激,因为这将帮助我理解如何实现递归。但是我也对其他解决方案持开放态度。

itertools.product()函数可能很有用。如果我们假设递归只有1级深度(ACE本身没有嵌套列表),那么我们可以使用以下方法:

来自itertools导入产品的

手=[[1,14],2,3,[1,14],7]
aces=[x代表手上的x,如果存在(x,列表)]
rest=[x表示手上的x,如果存在(x,int)]
组合=[列表(x)+产品中x的剩余(*aces)]
印刷品(组合)
收益率:

[[1,1,2,3,7]、[1,14,2,3,7]、[14,1,2,3,7]、[14,14,2,3,7]]

这可能有些过分,因为您只需要一个级别的递归(如@Aaron Keesing的答案),但这应该是可行的:

def iter_hands(hand, __current_index=0, __current_combo=None):
    __current_combo = __current_combo or []
    if __current_index == len(hand):
        yield __current_combo.copy()
        return

    choices = hand[__current_index]
    if not isinstance(choices, list):
        choices = [choices]
    for c in choices:
        __current_combo.append(c)
        yield from iter_hands(hand, __current_index + 1, __current_combo)
        __current_combo.pop()


def main():
    input_hand = [[1, 14], 2, 3, [1, 14], 7]
    want_hands = [
        [1, 2, 3, 1, 7],
        [1, 2, 3, 14, 7],
        [14, 2, 3, 1, 7],
        [14, 2, 3, 14, 7]
    ]
    got_hands = [hand for hand in iter_hands(input_hand)]

    assert want_hands == got_hands


if __name__ == '__main__':
    main()

嗯,有一种更简单的方法:

from itertools import product

product(*[i if isinstance(i, list) else [i] for i in hand])

我要求大家拿出一个更简单的解决方案

只要一点反馈,您就可以用包含ace的索引列表替换
True
/
False
列表(如图所示)。简言之,如果x==[1,14],
索引=[i代表i,x在enumerate(hand)中为x=[1,14]]
。现在,您只需迭代
索引
,而不用枚举
ACE
并检查它是否为
@JolonB是的,您是对的。我可以省下一点冗员。感谢您的反馈。我会声称,为了清晰起见,我一直在对该部分进行编码;)由于您的输出将是一个手的列表。。。从一开始就这样定义手可能是个好主意,
hand=[[1,14],2,3,[1,14],7]
@RichieV我确实会用这种方式定义手,因为我将迭代所有可能的手组合列表,以确定手是直的,还是齐平的,或者你有什么。我花了一段时间才明白,您正在将每张卡片都放入一个列表,这就是为什么
产品(*
有效…不会从集合中导入
。abc导入Iterable
,然后
isinstance(i,Iterable)
会更好吗?(或者同样,使用
序列
)而且,如果您想要一些过早的优化,您可能应该编写
*(…)
,而不是
*[…]
,以避免不必要的列表创建(请参见
dis.dis()
了解详细信息–这是一条额外的字节码指令;但是,您需要一条额外的
POP\u TOP
,因为
产生的值
返回
。(实际上,您可以用
(i,)
替换
[i]
,尽管我不确定这是否有什么作用。)@WIZWIZZ4您所有的建议都是正确的。检查
Iterable
将使这一点更一般,使用生成器表达式和元组将减少内存占用。但是,考虑到数据量,预期效果将很小。不过,我承认,这是一个“足够好”的方法在解决方案和生产中,最好坚持您指定的最佳实践。