GATE JAPE语法纯否定匹配规则未按预期命中问题排查
问题背景
我在编写JAPE语法,需要关联匹配三类注解:地名词典标注的Anatomy解剖结构注解、前置流程标注的Numeric数值注解、地名词典标注的Units单位注解,计划通过针对特定注解的纯否定规则,拦截不符合要求的匹配模式。
最初编写的JAPE语法如下:
Phase: Test Input: Anatomy Numeric Units Lookup Options: control=Appelt negationGrouping=false Rule: test_1 ( ( {Anatomy.minorType=="sinus_of_valsalva"} ):context // 拦截逻辑:context和value之间最多允许出现2个注解,这些注解不能是minorType为body_surface_area的Lookup注解,也不能是minorType为cm的Units注解 ( {!Lookup.minorType=="body_surface_area", !Units.minorType=="cm"} )[0,2] ( {Numeric} ):value ( {Units.minorType=="cm"} ):unit ):test --> :test { gate.AnnotationSet matchedVar =(gate.AnnotationSet) bindings.get("value"); gate.AnnotationSet matchedcontext=(gate.AnnotationSet) bindings.get("context"); gate.AnnotationSet matchedunit =(gate.AnnotationSet) bindings.get("unit"); gate.AnnotationSet matchedAnns =(gate.AnnotationSet) bindings.get("test"); gate.FeatureMap newFeatures = Factory.newFeatureMap(); newFeatures.put("vartype","test"); newFeatures.put("varValue", stringFor(doc, matchedVar)); newFeatures.put("context", stringFor(doc, matchedcontext)); if(matchedunit != null) { newFeatures.put("unit", stringFor(doc, matchedunit)); }else { newFeatures.put("unit", null); } outputAS.add(matchedAnns.firstNode(),matchedAnns.lastNode(),"test", newFeatures); }
测试用例表现
我在以下测试用例运行规则时出现了不符合预期的结果:
- 文本:
SoV 75 cm
预期输出:{vartype=test, context=SoV, value=75, unit=cm},实际可正常匹配 - 文本:
SoV other unimportant text 75 cm
预期输出:{vartype=test, context=SoV, value=75, unit=cm},实际可正常匹配 - 文本:
SoV body surface area 75 cm
预期结果:被拦截无匹配,实际可正常拦截 - 文本:
SoV is 75 cm
预期结果:正常匹配,该用例始终无法命中,原因不明

根据我之前的理解,{!Annotation X}这类纯否定写法的作用是匹配所有满足否定条件的输入注解,相关说明如下:
仅包含
{!Lookup}这类约束的规则实际效果可能和预期存在差异。任何仅包含否定约束的{...}匹配块,大致等价于对所有Input中声明的注解类型做或匹配,每个类型分支带上对应的否定约束:即等价于({A, !Lookup.....} | {B, !Lookup.....} | ....)的形式。
我怀疑自己对JAPE纯否定规则的核心逻辑存在理解偏差,需要定位问题原因。
可复现问题的完整JAPE代码
以下代码不需要依赖额外地名词典即可完整复现问题,所有标注逻辑都已内置:
Phase: AnatomyPhase Input: Token Options: control=Appelt negationGrouping=false Rule: anatomy ( {Token.string == "SoV"} ):anatomy --> :anatomy.Anatomy = {majorType = "aorta", minorType="sinus_of_valsalva", language="en"} Phase: UnitsPhase Input: Token Options: control=Appelt negationGrouping=false Rule: units ( {Token.string == "cm"} ):units --> :units.Units = {majorType = "length", minorType="cm", language="en"} Rule: units_2 ( {Token.string == "is"} ):units --> :units.Units = {majorType = "assertion", minorType="positive_assertion", language="en"} Phase: LookupPhase Input: Token Options: control=Appelt negationGrouping=false Rule: lookup Priority: 100 ( {Token.string == "body"}{Token.string == "surface"}{Token.string == "area"} ):lookup --> :lookup.Lookup = {majorType = "body_surface_area", minorType="body_surface_area", language="en"} Phase: TagNumeric Input: Token SpaceToken Split Lookup Options: control=Appelt negationGrouping=false Rule: double_tagger Priority: 100 ( {Token.kind == "number", Token notWithin Lookup} ({SpaceToken, !Split})[0,1] {Token.string ==~ "[.,]"} {Token.kind == "number", Token notWithin Lookup} ):double_tagger --> :double_tagger.Numeric = {type = "double"} Rule: int_tagger Priority: 99 ( {Token.kind == "number", Token notWithin Lookup} ):int_tagger --> :int_tagger.Numeric = {type = "integer"} Phase: Test Input: Lookup Numeric Units Anatomy Options: control=Appelt negationGrouping=false Rule: test_1 ( ( {Anatomy.minorType=="sinus_of_valsalva"} ):context ( {!Lookup.minorType=="body_surface_area", !Units.minorType=="cm"} )[0,2] ( {Numeric} ):value ( {Units.minorType=="cm"} ):unit ):test_var --> :test_var { gate.AnnotationSet matchedVar =(gate.AnnotationSet) bindings.get("value"); gate.AnnotationSet matchedcontext=(gate.AnnotationSet) bindings.get("context"); gate.AnnotationSet matchedunit =(gate.AnnotationSet) bindings.get("unit"); gate.AnnotationSet matchedAnns =(gate.AnnotationSet) bindings.get("test_var"); gate.FeatureMap newFeatures = Factory.newFeatureMap(); newFeatures.put("varType","test_var"); newFeatures.put("varValue", stringFor(doc, matchedVar)); newFeatures.put("context", stringFor(doc, matchedcontext)); if(matchedunit != null) { newFeatures.put("unit", stringFor(doc, matchedunit)); }else { newFeatures.put("unit", null); } outputAS.add(matchedAnns.firstNode(),matchedAnns.lastNode(),"test", newFeatures); }
内容的提问来源于stack exchange,提问作者Nicholas Sunderland
相关产品推荐
相关产品推荐

