4

我正在编写一个程序,试图从自然语言中获取意义。该程序将接受一个字符串,并查看它是否包含某些单词组合。有关示例,请参见以下代码片段:

if (phrase.contains("turn")) { // turn something on/off
    if (phrase.contains("on") && !phrase.contains("off")) { // turn something ON
        if (phrase.contains("pc") || phrase.contains("computer")) // turn on computer
            turnOnComputer();
        else if (phrase.contains("light") || phrase.contains("lamp")) // turn on lights
            turnOnLights();
        else
            badPhrase();
    }
    else if (phrase.contains("off") && !phrase.contains("on")) { // turn something OFF
        if (phrase.contains("pc") || phrase.contains("computer")) // turn off computer
            turnOffComputer();
        else if (phrase.contains("light") || phrase.contains("lamp")) // turn off lights
            turnOffLights();
        else
            badPhrase();
    }
    else {
        badPhrase();
    }
}
else {
    badPhrase();
}

如您所见,如果我想解释多个含义,这很快就会变成一团难以管理的代码。我怎样才能更好地管理这个?

4

5 回答 5

3

Apache OpenNLP是一个基于机器学习的工具包,用于处理自然语言文本。

它包括一个句子检测器、一个标记器、一个词性 (POS) 标记器和一个树库解析器

NLP手册

下载

希望能帮助到你 ; )

于 2013-05-08T08:33:42.040 回答
2

关键字发现当然只能用于非常小的单词集和/或非常受限的输入语言。好的,如果周围的文字无关紧要,也许也是如此。

但是,对于这种自然语言解析,您需要一种更复杂的方法,例如首先对文本进行标记,然后尝试查找单词之间的句法关系(从直接邻居开始,然后扩展范围)。最后,使用您找到的句法关系作为控制代码来驱动您的行动决策。

正则表达式可能不是这里的答案,因为它们需要非常严格的输入。考虑这样一个句子:

不要关灯,而是打开它。

RE 和你原来的方法都不会给你任何合理的结果。另外,不要忘记句法或语法错误。

于 2013-05-08T07:52:36.563 回答
1

首先,我不确定您的方法对自然语言处理的适用性如何。另外,不是已经有 NLP 的库了吗?特别是在 NLP 中,我知道有时顺序和词性很重要,而且这种方法对于单词变化不是很健壮。

但是,如果你想坚持你的方法,一个让它更易读和更可维护的想法(见下面更全面的优点/缺点)是这样的:

StringFinder finder = new StringFinder(phrase);
if        (finder.containsAll("turn", "on").andOneOf("computer", "pc").andNot("off").matches()) {
    turnOnComputer();
    return;
} else if (finder.containsAll("turn", "off").andOneOf("computer", "pc").andNot("on").matches()) {
    turnOffComputer();
    return;
} else if (finder.containsAll("turn", "on").andOneOf("light", "lamp").andNot("off").matches()) {
    ...
} else if (finder.containsAll("turn")) { // If we reached this point
    badPhrase();
} else if (...

有类似的东西:

class StringFinder {
    private final String phrase;
    private final Map<String, Boolean> cache = new HashMap<String, Boolean>();

    public StringFinder(String phrase) { this.phrase = phrase; }

    public StringFinder containsAll(String... strings) {
        for (String string : strings) {
            if (contains(string) == false) return new FailedStringFinder(phrase);
        }
        return this;
    }

    public StringFinder andOneOf(String... strings) {
        for (String string: strings) {
            if (contains(string)) return this;
        }
        return FailedStringFinder(phrase);
    }

    public StringFinder andNot(String... strings) {
        for (String string : strings) {
            if (contains(string)) return new FailedStringFinder(phrase);
        }
        return this;
    }

    public boolean matches() { return true; }

    private boolean contains(String s) {
        Boolean cached = cache.get(s);
        if (cached == null) {
            cached = phrase.contains(s);
            cached.put(s, cached);
        }
        return cached;
    }


}

class FailedStringFinder extends StringFinder {
    public boolean matches() { return false; }

    // The below are actually optional, but save on performance:
    public StringFinder containsAll(String... strings) { return this; }
    public StringFinder andOneOf(String... strings) { return this; }
    public StringFinder andNot(String... strings) { return this; }
}

缺点:

  • 重复检查:“转”被检查多次。
  • 重复模式(但请参阅下面的优点)。

优点:

  • 相对简洁的代码。
  • 检查被复制但被缓存,因此性能仍然很高。
  • 条件非常接近操作,导致代码非常易读。
  • 不嵌套条件允许在不重构代码的情况下更改特定操作所需的条件,从而产生更多可维护的代码。
  • 易于更改条件和操作出现的顺序,以控制优先级。
  • 缺少嵌套使得将来更容易并行化它。
  • 灵活的条件检查:例如,您可以向 StringFinder 添加方法以匹配重复检查,例如:public StringFinder containsOnAndNotOff() { return containsAll("on").andNot("off"); },或匹配您需要的一些奇异条件,例如andAtLeast3Of(String... strings) {...}.
    • 缓存也可以扩展为不仅记住单词是否出现,而且记住整个模式是否出现。
    • 您还可以添加最终条件:(andMatches(Pattern p)使用正则表达式模式) - 事实上,您可能可以使用正则表达式对许多其他检查进行建模。然后它会使缓存变得容易——而不是使用字符串作为键,而是使用模式。
于 2013-05-08T08:26:18.230 回答
1

使用正则表达式来实现您想要的,因为正则表达式可以匹配字符串组合。

于 2013-05-08T06:35:52.130 回答
0

这是@Oak 提供的答案中的固定代码

import java.util.HashMap;
import java.util.Map;

class StringFinder {
    private final String phrase;
    private final Map<String, Boolean> cache = new HashMap<String, Boolean>();

    public StringFinder(String phrase) { this.phrase = phrase; }

    public StringFinder containsAll(String... strings) {
        for (String string : strings) {
            if (contains(string) == false) return new FailedStringFinder(phrase);
        }
        return this;
    }

    public StringFinder andOneOf(String... strings) {
        for (String string: strings) {
            if (contains(string)) return this;
        }
        return new FailedStringFinder(phrase);
    }

    public StringFinder andNot(String... strings) {
        for (String string : strings) {
            if (contains(string)) return new FailedStringFinder(phrase);
        }
        return this;
    }

    public boolean matches() { return true; }

    private boolean contains(String s) {
        Boolean cached = cache.get(s);
        if (cached == null) {
            cached = phrase.contains(s);
            cache.put(s, cached);
        }
        return cached;
    }


}

class FailedStringFinder extends StringFinder {

    public FailedStringFinder(String phrase) {
        super(phrase);
    }

    public boolean matches() { return false; }

    // The below are actually optional, but save on performance:
    public StringFinder containsAll(String... strings) { return this; }
    public StringFinder andOneOf(String... strings) { return this; }
    public StringFinder andNot(String... strings) { return this; }
}
于 2013-05-08T16:36:24.160 回答