Python 使用递归的特殊嵌套列表

Python Special Nested List using Recursion

我有一个要放入嵌套列表中的函数和值列表。我希望结果是一个 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']

为了帮助阐明输出的外观,当我处理整个列表时,我会说 'the first item in the list is a function'。这样,如果它是一个单项列表,它可以只调用该函数,如果它是一个多项列表,我们知道剩余的项目(或项目列表)是该函数的参数。

使用上面较长句子示例中的所需输出列表:

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

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

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

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

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

我们调用 'place_order' 传递它想要的两个参数:第一个是计算 'toast' 函数的结果,将 'sandwich' 传递给 'toast' . 'place_order' 的第二个参数将是 calling/evaluating 'here'.

的结果

希望这是有道理的。 :)

如有任何帮助,我们将不胜感激!虽然我是新来的,但我已经阅读了发布问题的介绍,并希望提供了所需的一切。

谢谢!

欢乐

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

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']]