15

我一直在玩自然语言解析树并以各种方式操纵它们。我一直在使用斯坦福大学的 Tregex 和 Turgeon 工具,但代码很乱,不适合我主要使用 Python 的环境(这些工具是 Java,不适合调整)。我想要一个工具集,当我需要更多功能时可以轻松破解。是否有任何其他工具非常适合在树上进行模式匹配然后操作那些匹配的分支?

例如,我想将以下树作为输入:

(ROOT
  (S
    (NP
      (NP (NNP Bank))
      (PP (IN of)
        (NP (NNP America))))
    (VP (VBD used)
      (S
        (VP (TO to)
          (VP (VB be)
            (VP (VBN called)
              (NP
                (NP (NNP Bank))
                (PP (IN of)
                  (NP (NNP Italy)))))))))))

和(这是一个简化的例子):

  1. 查找具有标签 NP 的任何节点,该节点具有标签为 NP 的第一个子节点和一些名为“Bank”的后代,以及标签为 PP 的第二个子节点。
  2. 如果匹配,则取出 PP 节点的所有子节点并将它们移动到匹配的 NP 子节点的末尾。

例如,取树的这一部分:

(NP
  (NP (NNP Bank))
  (PP (IN of)
    (NP (NNP America))))

并将其变成这样:

(NP
  (NP (NNP Bank) (IN of) (NP (NNP America))))

由于我的输入树是 S 表达式,因此我考虑过使用 Lisp(嵌入到我的 Python 程序中),但它已经很长时间了,以至于我在 Lisp 中编写了任何重要的东西,以至于我什至不知道从哪里开始。

什么是描述模式的好方法?什么是描述操作的好方法?考虑这个问题的好方法是什么?

4

3 回答 3

11

美在旁观者的眼中。但是你永远不会说Tregex 或 Turgeon 的代码是如何一团糟的。这听起来更像是您无法处理 Java 或更大的抽象,因此您正在寻找用 Python 编写的具体内容。

手写树匹配和转换功能没有错。事实上,我们过去一直这样做。但是在前几百个之后,似乎必须有更好的方法,因此我们转向使用 Tregex 和 Turgeon 的领域特定语言。这通常被视为一种值得称赞的编程风格。参见维基百科。它们是具有精确语法规范等的指定语言。这是您使用它们的示例。

Tree t = Tree.valueOf("(ROOT (S (NP (NP (NNP Bank)) (PP (IN of) (NP (NNP America)))) (VP (VBD used) (S (VP (TO to) (VP (VB be) (VP (VBN called) (NP (NP (NNP Bank)) (PP (IN of) (NP (NNP Italy)))))))))))");
TregexPattern pat = TregexPattern.compile("NP <1 (NP << Bank) <2 PP=remove");
TsurgeonPattern surgery = Tsurgeon.parseOperation("excise remove remove");
Tsurgeon.processPattern(pat, surgery, t).pennPrint();

请注意,Java 代码实际上比 Lisp 代码更短,这正是因为使用了特定领域的语言。很难看出这怎么可能更简单:指定模式、指定操作、应用。

但是,如果您更喜欢手写方法来匹配树上的模式并将它们更改为 Python 中的其他树,那么非常欢迎您去做。

于 2010-09-18T21:25:22.617 回答
8

这是使用 Lisp 的典型案例。您需要一个将另一个函数映射到树上的函数。

这是一个使用 Common Lisp 的过程匹配示例。Lisp 中有可以处理列表结构的匹配器,可以使用这些匹配器。使用列表匹配器可以简化示例(有关使用模式匹配器的示例,请参阅我的其他答案)。

编码:

(defun node-children (node)
  (rest node))

(defun node-name (node)
  (second node))

(defun node-type (node)
  (first node))


(defun treemap (tree matcher transformer)
  (cond ((null tree) nil)
        ((consp tree)
         (if (funcall matcher tree)
             (funcall transformer tree)
           (cons (node-type tree)
                 (mapcar (lambda (child)
                           (treemap child matcher transformer))
                         (node-children tree)))))
        (t tree))))

这个例子:

(defvar *tree*
  '(ROOT
    (S
     (NP
      (NP (NNP Bank))
      (PP (IN of)
          (NP (NNP America))))
     (VP (VBD used)
         (S
          (VP (TO to)
              (VP (VB be)
                  (VP (VBN called)
                      (NP
                       (NP (NNP Bank))
                       (PP (IN of)
                           (NP (NNP Italy))))))))))))



(defun example ()
  (pprint
   (treemap *tree*
            (lambda (node)
              (and (= (length (node-children node)) 2)
                   (eq (node-type (first (node-children node))) 'np)
                   (some (lambda (node)
                           (eq (node-name node) 'bank))
                         (children (first (node-children node))))
                   (eq (first (second (node-children node))) 'pp)))
            (lambda (node)
              (list (node-type node)
                    (append (first (node-children node))
                            (node-children (second (node-children node)))))))))

运行示例:

CL-USER 75 > (example)

(ROOT
 (S
  (NP
   (NP (NNP BANK) (IN OF) (NP (NNP AMERICA))))
  (VP
   (VBD USED)
   (S
    (VP
     (TO TO)
     (VP
      (VB BE)
      (VP
       (VBN CALLED)
       (NP
        (NP
         (NNP BANK)
         (IN OF)
         (NP (NNP ITALY)))))))))))
于 2010-09-12T07:19:14.977 回答
5

这是 Common Lisp 的第二个版本。这次我使用的是模式匹配器

我正在使用一个将模式与 Lisp 数据匹配的函数。PMATCH:MATCH 是 Winston/Horn, Lisp, 3rd Edition 一书中的模式匹配器的增强版本。有类似的模式匹配功能可用。

数据与我的其他答案一样。

树映射函数更改为使用模式匹配器。如果匹配成功,则 PMATCH:MATCH 函数返回 T 或绑定的关联列表。如果匹配不成功,则返回 NIL。PMATCH:INSTANTIATE-PATTERN 采用一个模式和一组绑定。它返回一个新的列表结构,其中模式变量被替换为绑定。

(defun treemapp (tree pattern transformer)
  (cond ((null tree) nil)
        ((consp tree)
         (let ((bindings (pmatch:match pattern tree)))
           (if bindings
               (pmatch:instantiate-pattern transformer bindings)
             (cons (node-type tree)
                   (mapcar (lambda (child)
                             (treemapp child pattern transformer))
                           (node-children tree))))))
        (t tree)))

示例使用 now 模式。

该模式是一个列表结构。#?symbol 匹配单个项目并为符号创建绑定。#$symbol 匹配项目列表并为符号创建绑定。

转换器是一种将使用绑定实例化的模式。

(defun example1 ()
  (pprint (treemapp *tree*
                    '(NP (NP (#?type bank)) (PP #$children))
                    '(NP (NP (#?type bank) #$children)))))

运行此代码返回与我的其他答案相同的结果。

于 2010-09-18T22:15:29.777 回答