【问题标题】:Python Special Nested List using Recursion使用递归的 Python 特殊嵌套列表
【发布时间】:2021-08-15 05:59:55
【问题描述】:

我有一个想要放入嵌套列表的函数和值列表。我希望结果是一个 LISP 样式列表(看起来接近某些 LISP 样式可执行代码的东西),我以后可以轻松处理。 该列表来自一个“句子”,它按单词拆分为一个列表 - 它通过首先检查数据库中的任何多单词标记,粘合它们然后稍后将它们分开,将定义的短语(多单词)保持在一起。 任何不在数据库中的单词都将被忽略。标点符号也被忽略。它只匹配实际存储的令牌。 这让我可以写一个可以翻译成我以后可以处理的函数的句子。 我们可以忽略这些是函数的事实,而将它们视为字符串,因为它们是这样存储的,但是函数和参数完美地描述了用例,因为它们就是这样。 函数将是列表中的第一项,然后是同一列表中的参数。 一个没有参数的函数将在一个只有那个元素的列表中。 一个本身是函数的参数将在一个列表中(如果有的话,还有它的参数)。 每个函数采用的参数数量是预设的,并且不会改变(它可以调用其他接受自己参数的函数)。 从技术上讲,应该能够无限深入,尽管我确信如果限制有帮助,几个级别就足够了。这是一个递归类型的问题,因此深度级别并不重要。 如果有帮助,我正在使用 Django,因此可以访问那里的任何模型方法,因为 Token 和句子一样是模型。

我将列表项称为“令牌”。它们可以不止一个词。它们实际上存储在数据库中。 每个 Token 可以有:symbol, val, kind 符号:在句子中搜索的漂亮格式字符串 价值:我们想要的东西在列表中 种类:整数;其他种类的参数或代码的数量

KIND_CHOICES = [ (0, 'Argless Function'), (1, '1-Arg Function'), (2, '2-Arg Function'), (3, '3-Arg Function'), (4, '4-Arg Function'), (6, 'Value'), (7, 'Ignore'), ]

让我们以这些令牌为例: ("符号",'val',种类)

("Walk",'walk',1) ("To",'to',1) ("Sandwich Shop",'sandwich-shop',6) ("Order",'place_order',2) ("Toasted",'toast',1) ("Sandwich",'sandwich',6) ("Dine-In",'here',0) ("Eat",'eat',1) ("Back",'back',1) ("Home",'residence',6) ("Nap",'sleep',0) ("on the sofa",7)

这是一个例句: Walk To the Sandwich Shop, Order your favorite Toasted Sandwich for Dine-In, Eat your Sandwich, Walk Back Home, then finally Nap on the sofa.

我将从当前的工作清理函数中得到的第一个列表为我们提供: ['walk','to','sandwich-shop','place_order','toast','sandwich','here','eat','sandwich','walk','back','residence','sleep']

然后,最后(我无法正确的部分!我错了一个,得到重复,缺少标记或错误的结构)

[['walk',['to','sandwich-shop']],['place_order',['toast','sandwich'],['here']],['eat','sandwich'],['walk',['back','residence']],['sleep']]

我的一些尝试涉及使用重复的占位符字符串作为参数,以及各种 replace_or_append 实现尝试;在参数列表中插入空元素,然后使用 put_in_next_empty_spot 实现(尝试几次);以及一些带有递增索引和弹出的更简单的循环。 我只是因为某种原因被困在这个问题上,并且可以使用一些脑力来解决看起来应该是一个非常简单的问题。

以下是一次非常失败的尝试中的一些示例代码:

def nest(self, token, l, idx):
    if token.is_func:
        result = [token.val]
        if token.arg_count:
            for i in range(token.arg_count):
                idx += 1
                f = self.funcs.pop(idx)
                t = self.get_token(f)
                result = self.nest(t,result,idx)
        l.append(result)
    else:
        l.append(token.val)
    return l

def nestify(self):
    nested = []
    for i, s in enumerate(self.funcs):
        token = self.get_token(s)
        nested = self.nest(token,nested,i)
    return nested

我使用了一些我添加到 Token 的便捷方法来检查它的 .kind 属性(整数),获取 Token 对象等。[self.funcs] 这是上面提到的第一个函数列表(清理平面列表)。

原句: "Walk To Sandwich Shop Dine-In Nap"

变成(这行得通,也是我们想要的): ['walk', 'to', 'sandwich-shop', 'here', 'nap']

应该变成: [['walk', ['to', 'sandwich-shop']], ['here'], ['nap']]

但不幸的是,我得到: ['walk', ['to', ['here'], ['nap']], 'sandwich-shop']

为了帮助阐明输出的外观,当我处理整个列表时,我会说“列表中的第一项是一个函数”。这样,如果它是一个单项列表,它可以只调用该函数,如果它是一个多项列表,我们知道剩余的项(或项列表)是该函数的参数。

使用上面较长句子示例中我们想要的输出列表:

[['walk',['to','sandwich-shop']],['place_order',['toast','sandwich'],['here']],['eat','sandwich'],['walk',['back','residence']],['sleep']]

如果我们取第一个项目(列表):

['walk',['to','sandwich-shop']]

我们将处理传入一个 arg 的“walk”函数:评估函数“to”的结果,将参数“sandwich-shop”传递给它。

如果我们取第二项(另一个列表): ['place_order',['toast','sandwich'],['here']]

我们将调用 'place_order' 传递给它想要的两个参数:第一个将是评估 'toast' 函数的结果,将 'sandwich' 传递给 'toast'。 'place_order' 的第二个参数将是调用/评估 'here' 的结果。

希望这是有道理的。 :)

这里的任何帮助将非常感激!虽然我是新来的,但我已经阅读了发布问题的介绍,并希望能提供所需的一切。

谢谢!

快乐

【问题讨论】:

  • 请提供预期的minimal, reproducible example (MRE)。我们应该能够复制和粘贴您的代码的连续块,执行该文件,并重现您的问题以及跟踪问题点的输出。这让我们可以根据您的测试数据和所需的输出来测试我们的建议。显示中间结果与您的预期不同的地方。您发布的代码定义了两个函数并在不调用任何一个的情况下停止。
  • 请从intro tour 重复on topichow to ask。您的大部分帖子都是对您的项目的描述,其中大部分与手头的问题无关。转储多余的部分,正确跟踪您的代码,并发布错误点周围的证据。
  • 我已经这样做了。如果不是,请更具体。
  • 在你想要的第一个输出中,它不应该是像 [['walk', [['to', ['sandwich-shop']], ['place_order', [['toast', ['sandwich']], ...], ...] 这样的东西,因为 to 是一个单参数函数,所以它应该消耗 sandwich-shop (或者,就此而言,place-order )?您能否澄清to 没有被列为带有参数的自己的函数,而是列为Value
  • 我想你可能是对的 AJAX!我可能已经为此工作了很长时间,我只是在这一点上让它变得更糟。

标签: python list recursion lisp nested-loops


【解决方案1】:

要根据参数规范构建嵌套列表,您可以使用collections.deque 的递归。通过使用对 deque 的引用传递给 nest_tokens,您可以通过弹出“函数”所需的参数数量来改变标记化的结果:

from collections import deque
def tokenize(s, tokens):
   while s:
      if (o:=[(a, b) for a, *b in tokens if s.startswith(a)]):
         j, k = max(o, key=lambda x:len(x[0]))
         yield k
         s = s[len(j):]
      else:
         s = s[1:]
      
def nest_tokens(tokens, to_d = None):
   c = 0
   while tokens and (to_d is None or c < to_d):
      if (n:=tokens.popleft())[1] == 6:
         yield n[0] #found a value, yield result
      else:
         #found an argument token, yield back as list if empty, otherwise -
         #recursively call "nest_tokens" on "tokens" to produce the arguments
         yield [n[0]] if not n[1] else [n[0], *nest_tokens(tokens, n[1])]
      c += 1

def nest_tokenized(s, tokens, choice):
   c = dict(choice)
   t = [(*i, l) for i in tokenize(s, tokens) if (l:=c.get(i[-1])) != 'Ignore']
   return list(nest_tokens(deque(t)))

KIND_CHOICES = [(0, 'Argless Function'), (1, '1-Arg Function'), (2, '2-Arg Function'), (3, '3-Arg Function'), (4, '4-Arg Function'), (6, 'Value'), (7, 'Ignore')]
tokens = [("Walk",'walk',1), ("To",'to',1), ("Sandwich Shop",'sandwich-shop',6), ("Order",'place_order',2), ("Toasted",'toast',1), ("Sandwich",'sandwich',6), ("Dine-In",'here',0), ("Eat",'eat',1), ("Back",'back',1), ("Home",'residence',6), ("Nap",'sleep',0), ("on the sofa",7)]
s = 'Walk To the Sandwich Shop, Order your favorite Toasted Sandwich for Dine-In, Eat your Sandwich, Walk Back Home, then finally Nap on the sofa.'
s1 = "Walk To Sandwich Shop Dine-In Nap"
print(nest_tokenized(s, tokens, KIND_CHOICES))
print(nest_tokenized(s1, tokens, KIND_CHOICES))

输出:

[['walk', ['to', 'sandwich-shop']], ['place_order', ['toast', 'sandwich'], ['here']], ['eat', 'sandwich'], ['walk', ['back', 'residence']], ['sleep']]
[['walk', ['to', 'sandwich-shop']], ['here'], ['sleep']]

【讨论】:

  • 这是美丽的阿贾克斯!我想我确实表达了我的意图有点不正确。此处标记为“值”的那些标记不会被包装在列表中,列表中的第一个函数也不会(如 Clojure 语法),因此当我遍历列表时,我可以询问当前元素是否为列表看看它是一个函数还是只是一个要传入的参数。当我把它写下来时,我仍然需要编写那个迭代器哈。真的很感激这方面的帮助!
  • 所以输出最终应该是:[['walk',['to','sandwich-shop']],['place_order',['toast','sandwich'],['here']],['eat','sandwich'],['walk',['back','residence']],['sleep']] 因为我认为这将是最容易迭代然后评估的。再次,非常感谢任何帮助! :)
  • @Joy 澄清一下,最后一个输出中的dine 不应该是here,因为"Dine-In" 映射到您的令牌中的"here"
  • 是的,很抱歉那个错字。在这种情况下,它确实应该输出“这里”:) 我以为我把它们都抓住了,抱歉哈哈 更新了上面的代码以反映这一点(认为我把它们都抓住了)
  • 我添加了一些列表最终将如何处理的示例,以帮助阐明输出的外观和原因。再次感谢您在这里的任何帮助! :)
猜你喜欢
  • 2014-03-06
  • 2021-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-24
  • 2021-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多