我如何操纵解析树?

gui*_*ism 15 lisp nlp pattern-matching stanford-nlp s-expression

我一直在玩自然语言解析树并以各种方式操纵它们.我一直在使用斯坦福大学的Tregex和Tsurgeon工具,但代码很混乱,并不适合我的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)))))))))))
Run Code Online (Sandbox Code Playgroud)

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

  1. 找到标签为NP的任何节点,其中第一个孩子的标签为NP,一些后代名为"Bank",第二个孩子的标签为PP.
  2. 如果匹配,则获取PP节点的所有子节点并将它们移动到匹配的NP子节点的末尾.

例如,采取树的这一部分:

(NP
  (NP (NNP Bank))
  (PP (IN of)
    (NP (NNP America))))
Run Code Online (Sandbox Code Playgroud)

把它变成这个:

(NP
  (NP (NNP Bank) (IN of) (NP (NNP America))))
Run Code Online (Sandbox Code Playgroud)

由于我的输入树是S表达式,我考虑使用Lisp(嵌入到我的Python程序中)但是我已经写了很长时间,我在Lisp中编写了一些重要内容,我不知道从哪里开始.

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

Chr*_*ing 11

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

手写树匹配和转换函数没有任何问题.实际上,我们过去常常这样做.但在前几百个之后,似乎必须有一个更好的方法,因此我们开始使用Tregex和Tsurgeon的领域特定语言.这通常被视为一种值得称赞的编程风格.请参阅维基百科.它们是具有精确语法规范的精心设计的语言,等等.以下是使用它们的示例.

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();
Run Code Online (Sandbox Code Playgroud)

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

但是,如果您更喜欢手工编写与树上的模式匹配的方法并将其更改为Python中的其他树,那么您最欢迎这样做.


Rai*_*wig 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))))
Run Code Online (Sandbox Code Playgroud)

这个例子:

(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)))))))))
Run Code Online (Sandbox Code Playgroud)

运行示例:

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)))))))))))
Run Code Online (Sandbox Code Playgroud)


Rai*_*wig 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)))
Run Code Online (Sandbox Code Playgroud)

示例现在使用模式.

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

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

(defun example1 ()
  (pprint (treemapp *tree*
                    '(NP (NP (#?type bank)) (PP #$children))
                    '(NP (NP (#?type bank) #$children)))))
Run Code Online (Sandbox Code Playgroud)

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