【问题标题】:How to make TuProlog recognize invalid facts?如何让 TuProlog 识别无效事实?
【发布时间】:2016-02-04 06:37:42
【问题描述】:

我有以下两个 Prolog 文件:

ontology.pl:

isSite(Url) :- string(Url).
guestPostPublished(GuestPostId, Date, Site, Url) :-
 string(GuestPostId),
 date(Date),
 isSite(Site),
 string(Url),
 \+(guestPostPublished(GuestPostId, _, _, _)).

invalidFile.pl:

isSite('somesite.com').
guestPostPublished(
    'gp1',
    date(2016,2,2),
    'somesite.com',
    'someUrl').

guestPostPublished(
    'gp1',
    date(2016,2,2),
    'somesite.com',
    'anotherUrl').

invalidFile.pl 无效,因为它违反了ontology.pl 中指定的所有GuestPostIds 必须是唯一的规则。

当我将该数据加载到我的引擎中时,我会抛出一些异常,表明数据无效。但事实并非如此。

我做错了什么?如何确保当我将无效数据提供给 TuProlog 引擎时,我会收到某种通知(例如异常或某些标志)?

这是我的代码的相关片段(你可以找到整个代码here):

@Test
public void test2() throws InvalidObjectIdException, IOException,
        MalformedGoalException, InvalidTheoryException, UnknownVarException, NoSolutionException,
        NoMoreSolutionException, InvalidLibraryException {
    final Prolog engine = createEngine();

    try
    {
        loadPrologFiles(engine, new String[]{
                "src/main/resources/ontology.pl",
                "src/main/resources/invalidFile.pl"
        });
        Assert.fail("Engine swallows invalid Prolog file.");
    }
    catch (final Exception exception) {
        // TODO: Check that the right exception is thrown
    }
    final List<String> result = getResults(engine, "guestPostPublished(_,X,_,_).", "X");
    System.out.println("result: " + result);
}

private Prolog createEngine() throws InvalidObjectIdException {
    final Prolog engine = new Prolog();
    engine.addOutputListener(new OutputListener() {
        public void onOutput(OutputEvent outputEvent) {
            System.out.println(String.format("PROLOG: %s", outputEvent.getMsg()));
        }
    });
    Library lib = engine.getLibrary("alice.tuprolog.lib.OOLibrary");
    ((OOLibrary)lib).register(new Struct("stdout"), System.out);
    return engine;
}

private void loadPrologFiles(final Prolog engine, final String[] files) throws IOException, InvalidTheoryException {
    final List<String> paths = Arrays.asList(files);
    final StringBuilder theoryBuilder = new StringBuilder();

    for (final String path : paths) {
        theoryBuilder.append(System.lineSeparator());
        theoryBuilder.append("% ");
        theoryBuilder.append(path);
        theoryBuilder.append(" (START)");
        theoryBuilder.append(System.lineSeparator());
        theoryBuilder.append(FileUtils.readFileToString(new File(path)));
        theoryBuilder.append(System.lineSeparator());
        theoryBuilder.append("% ");
        theoryBuilder.append(path);
        theoryBuilder.append(" (END)");
        theoryBuilder.append(System.lineSeparator());
    }

    final Theory test1 = new Theory(theoryBuilder.toString());
    engine.setTheory(test1);
}

private List<String> getResults(final Prolog engine, final String query, final String varName) throws
        MalformedGoalException, NoSolutionException, UnknownVarException, NoMoreSolutionException {
    SolveInfo res2 = engine.solve(query);

    final List<String> result = new LinkedList<String>();
    if (res2.isSuccess()) {
        result.add(res2.getTerm(varName).toString());
        while (engine.hasOpenAlternatives()) {
            res2 = engine.solveNext();
            final Term x2 = res2.getTerm("X");
            result.add(x2.toString());
        }
    }
    return result;
}

【问题讨论】:

  • 通常的 Prolog 没有像关系数据库那样内置的数据完整性约束机制。除非 TuProlog 将其作为库提供,否则您不能只声明主键。你可以很容易地自己实现它,但是:数据库是动态的吗?它会在程序的生命周期内发生变化吗?您需要在该表中插入新行吗?
  • @Boris Prolog 文件在运行时不会更改。我想让程序做的是:读取一组 Prolog 文件并告诉我,它们是否符合ontology.pl 中指定的规则。然后,程序终止。
  • 如果您咨询 invalidFile.pl,Prolog 假定内容建立事实 并且根据定义是真实的。如果您想将invalidFile.pl 的内容视为查询,那么您需要读取文件并尝试调用每个事实并检查调用是否成功。
  • 这取决于更广泛的背景,哪种方法更适合您的应用程序。我的建议假设您将有一个谓词,例如,读取一个试验事实文件并尝试调用它们,并且会(以某种方式)让您知道是否有失败,或者将提供失败事实的列表,或者任何你想要的(你还没有真正说过你想如何处理失败,单独或en masse)。 Boris 的解决方案假定您的数据库中已经有事实断言(例如,您已经咨询过invalidFile.pl),您想质疑其有效性。
  • 如果您想在咨询/断言其中有任何问题之前检查文件,那么我的建议将适用。

标签: java prolog tuprolog


【解决方案1】:

要对 Prolog 事实表设置数据完整性约束,您需要以不同的方式处理此问题。我建议您首先尝试在纯 Prolog 中执行此操作,不使用 Java 位,只是为了了解正在发生的事情。

如果数据库是静态的并且不会更改,这很容易:只需加载它,然后针对它运行查询以进行数据完整性检查。例如,您有一个表 site/1,其中只有一列,并且您希望确保所有值都是字符串:

没有site(S),所以S 不是字符串

\+ ( site(S), \+ string(S) )

如果你想把它包装成一个谓词,你必须用一个不同于你的表的名字来命名谓词!

site_must_be_string :-
    \+ ( site(S), \+ string(S) ).

或者,对于另一个,一个唯一的列(主键):

guest_post_published/4 的第一个参数中没有重复项

findall(ID, guest_post_published(ID, _, _, _), IDs),
length(IDs, Len),
sort(IDs, Sorted),   % sort/2 removes duplicates!
length(Sorted, Len). % length does not change after sorting

您可能还需要将其包装在它自己的谓词中。

【讨论】:

    【解决方案2】:

    如果您想在断言“被指控的”事实之前检查它们的有效性,则需要阅读而不是查阅该文件并尝试调用每个被指控的事实以查看它是否成功。

    作为一个非常简单的示例,您可以执行以下操作:

    open('invalidFile.pl', read, S),
    read(S, TestFact),
    call(TestFact).
    

    如果根据您现有的事实和规则从invalidFile.pl 读取的术语成功,则call(TestFact) 将成功,否则它将失败。您可以使用此序列并阅读所有所谓的事实并对其进行测试:

    validate_file(File) :-
        open(File, read, S),
        read_terms(S, Terms),
        maplist(call, Terms),   % This will fail if *any* term fails
        close(S).
    
    read_terms(Stream, []):- 
        at_end_of_stream(Stream). 
    
    read_terms(Stream, [Term|Terms]):- 
        \+  at_end_of_stream(Stream), 
        read(Stream, Term), 
        read_terms(Stream, Terms).
    

    在这种情况下,如果文件中的 any 项为 false,validate_file 将失败。作为一个练习,您可以通过在read_terms 中跟踪“术语计数”或类似的内容来使其更智能,并编写一个谓词来检查术语并在它失败时反馈术语编号,以便您可以查看哪个(S)失败。

    【讨论】:

      猜你喜欢
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-12
      • 1970-01-01
      • 1970-01-01
      • 2018-10-31
      相关资源
      最近更新 更多