1

我创建了一个包含不同类型类别的记录,但这导致 GFgfo在编译超过 10 分钟后生成巨大的文件 (150Mb) 甚至在某些语言中崩溃。我的记录只包含 9 个元素(8 个不同的类别)。

以下面的小例子为例,假设我想使用 GF 创建一个自我介绍文本。为了生成这样的文本,我创建了一条记录并将其命名为 Person。Person 记录将包含不同的信息,例如姓名、年龄、地址、爱好、人的特征等。仅创建这个小记录就使 GF 生成了一个 1.28 Mb 的gfo文件,编译它需要超过一秒钟的时间。

这是我的代码

摘要test.gf

abstract Test = {
    flags
        startcat = Sentence;
    cat
        Sentence; Human;

    fun
        MySentence : Human -> Sentence; 
        Joan : Human;   
}

混凝土TestSpa.gf

concrete TestSpa of Test = open SyntaxSpa, ParadigmsSpa, Predef, DictSpa in{
    lincat
        Sentence = Text;
        Human = Person;
    lin
        MySentence person =  generateIntro person;


        Joan = {name = "Joan" ; det = aSg_Det ; job = mkN ("Doctor") ; age = mkNumeral("22") ; hobby = bailar_V ; lastMeal = mkV2(hablar_V) ; 
                food = mkN ("spaghetti") ; prep = mkPrep ("at") ; location = mkN("resturant") ; meal = Lunch ; feeling = mkA("happy")};



    oper
        Person : Type = {name : Str ;
                            det : Det;
                            job : N ;
                            age : Numeral ;
                            hobby : V ;
                            lastMeal : V2 ;
                            food : N;
                            prep : Prep;
                            location : N;
                            meal : Meal;
                            feeling : A};

        generateIntro : Person -> Text =
            \per -> mkText (mkUtt (mkNP (mkDet(i_Pron)) (per.job)));

    param 
        Meal = Breakfast | Lunch | Dinner;

}

我注意到某些类别在添加到一个记录中时会导致运行时间变长,但其他类别不会产生太大影响。

问题

1-除了使用记录将不同的类别值保持在一起,但不使用抽象的依赖类型之外,还有其他方法吗?

2-有没有办法解决这个问题,让GF知道记录中的这些值都是需要的,但它们不会出现在每个句子中?

4

2 回答 2

1

是的,第二个版本更小,因为每个 lincat 的字段更少。原来是:

Person : Type = {name : Str ;
                 det : Det;
                 job : N ;
                 age : Numeral ;
                 hobby : V ;
                 lastMeal : V2 ;
                 food : N;
                 prep : Prep;
                 location : N;
                 meal : Meal;
                 feeling : A};

在你的新版本中,这个非常大的 lincat 被分解成越来越小的。这就是 PGF 较小的原因。

具体类别

首先,一点背景。我在这里有一篇关于该主题的完整博客文章,如果您有兴趣,我建议您阅读。但简短的版本是:

  • 每个 GF 类别都可能编译成多个具体类别。具体类别的数量取决于固有参数的数量。例如:
param
  Bool = True | False ;
lincat
  MyCat = {s : Str ; b : Bool} ;

该类别MyCat成为 PGF 中的 2 个具体类别:一个b字段为 True,另一个为 False。

现在查看您在 .lincat 中使用的 RGL 类别的具体类别Person。该name字段只是一个 Str,所以它没有贡献。但接下来是det : Det。在 RGL 的浪漫语言中,限定符具有数字的固有参数以及它们是否为负:请参阅此处的实现。所以我们得到 4 的 Det。接下来我们有 N:罗曼语语言具有名词的固有性别,因此 N 编译为 2 个具体类别。让我用数字注释其余部分:

Person : Type = {name : Str ;    -- 1
                 det : Det;      -- 4
                 job : N ;       -- 2
                 age : Numeral ; -- 2
                 hobby : V ;     -- 2
                 lastMeal : V2 ; -- 16
                 food : N;       -- 2
                 prep : Prep;    -- 8
                 location : N;   -- 2
                 meal : Meal;    -- 3
                 feeling : A};   -- 8

这里我们得到乘法 4 * 2 * 2 * 2 * 16 * 2 * 8 * 2 * 3 * 8,它等于393,216个具体类别Person

相比之下,您的新 4 种类型要小得多:

  • 人 1:1 * 4 * 2 = 8
  • 人 2:2 * 2 * 16 = 64
  • 人 3:2 * 8 * 2 = 32
  • 人 4:3 * 8 = 24

所以总共有 8 + 64 + 32 + 24 = 128 个具体类别用于不同的人。

我的建议

如果我正在编写这个语法,我会让语法生成所有组合,并将它们限制在外部编程语言中。

的类别Person将只包括名称,而所有其他事物,如工作、爱好和膳食将是单独cat的 s 和funs。例如:

cat
  Person ; Hobby ; Meal ; Sentence ;
fun
  Joan, Maria : Person ;
  Singing, Dancing : Hobby ;
  Pizza, Dumplings : Meal ;

  hasHobby : Person -> Hobby -> Sentence ;
  favFood : Person -> Meal -> Sentence ;

该语法将构建人、爱好和膳食的所有组合——也许在现实生活中只有琼唱歌和玛丽亚跳舞,但该语法也会生成“琼喜欢跳舞”和“玛丽喜欢唱歌”。

这种设计的好处是:

  • 每个 lincat 都很小,通常只有一个 RGL 类别,或者如果需要,可能有 1-2 个自定义参数。(例如,类似的东西Meal不是 RGL 参数,它是为此应用程序自定义创建的。)
  • 每个函数只需要几个参数,因此 PGF 中只有几个具体的函数。

这种语法比 Yousef 帖子中的限制性版本生成更多的句子,但 PGF 小得多。这就是GF的工作方式。

那么在我的外部应用程序中,我将控制生成和线性化哪些 GF 树。我可能有一个电子表格来存储人们最喜欢的食物和爱好,并且从该电子表格中我可以生成 GF 树。例如:

# people.csv
Person,favFood,hasHobby
"Maria","dumplings","dancing"
"Joan","pizza","singing"

然后编写脚本生成GF树:

favFood Maria Dumplings
hasHobby Maria Dancing
favFood Joan Pizza
hasHobby Joan Singing
于 2021-09-20T07:23:15.527 回答
0

在询问并进行了一些调查之后,我发现这个技巧可能会起作用,但肯定它看起来确实很丑陋,但如果它有效,它并不愚蠢。

我所做的是将记录分成多个记录,如下所示:

摘要Test.gf

abstract Test = {
    flags
        startcat = Sentence;
    cat
        Sentence; 
        Relation;
        Human1 Relation;
        Human2 Relation;
        Human3 Relation;
        Human4 Relation;

    fun
        MySentence : (r : Relation) -> Human1 r -> Human2 r -> Human3 r -> Human4 r -> Sentence;
        Joan1 : Human1 RelA;
        Joan2 : Human2 RelA;
        Joan3 : Human3 RelA;
        Joan4 : Human4 RelA;
        RelA : Relation;
}

混凝土TestSpa

concrete TestSpa of Test = open SyntaxSpa, ParadigmsSpa, Predef, DictSpa in{
    lincat
        Sentence = Text;
        Human1 = Person1;
        Human2 = Person2;
        Human3 = Person3;
        Human4 = Person4;
    lin
        MySentence _ person1 person2 person3 person4 =  generateIntro person1 person2 person3 person4;


        Joan1 = {name = "Joan" ; det = aSg_Det ; job = mkN ("Doctor")};
        Joan2 = {age = mkNumeral("22") ; hobby = bailar_V ; lastMeal = mkV2(hablar_V)};
        Joan3 = {food = mkN ("spaghetti") ; prep = mkPrep ("at") ; location = mkN("resturant")};
        Joan4 = {meal = Lunch ; feeling = mkA("happy")};

        RelA = {s = ""};

    oper
        Person1 : Type = {name : Str ;
                            det : Det;
                            job : N ;};
        Person2 : Type = {age : Numeral ;
                            hobby : V ;
                            lastMeal : V2};
        Person3 : Type = {food : N;
                            prep : Prep;
                            location : N};
        Person4 : Type = {meal : Meal;
                            feeling : A};



        generateIntro : Person1 -> Person2 -> Person3 -> Person4 -> Text =
            \per1, per2, per3, per4 -> mkText (mkUtt (mkNP (mkDet(i_Pron)) (per1.job)));

    param 
        Meal = Breakfast | Lunch | Dinner;

}

在这个版本中,gfo文件只有 9 Kb 大。我会尽快更新这个版本的代码。

于 2021-09-11T10:52:49.960 回答