1.修复语法错误
您的代码中的所有错误都不是由依赖类型引起的。第一个问题,实际的语法错误是这样的:
To_S pre = {s = pre.s} ;
pre
是GF中的保留字,不能作为变量名使用。你可以写例如
To_S pr = {s = pr.s} ;
或更短——因为To_S
看起来像一个强制函数,你可以这样做:
To_S pr = pr ;
修复后,您会收到新的错误消息:
Happened in linearization of Pred
type of sub
expected: Str
inferred: {s : Str}
Happened in linearization of Compl
type of ver
expected: Str
inferred: {s : Str}
Happened in linearization of Compl
type of obj
expected: Str
inferred: {s : Str}
这些固定如下。您不能将 a{s : Str}
放入应该包含 Str 的字段中,因此您需要访问 sub .s、 ver .s和 obj .s。
Pred _ sub vp = {s = sub.s ++ vp.fst ++ vp.snd} ;
Compl _ ver obj = {fst = ver.s ; snd = obj.s} ;
修复语法错误后的完整语法
这对我有用。
lin
To_S pr = pr ;
Pred _ sub vp = {s = sub.s ++ vp.fst ++ vp.snd} ;
Compl _ ver obj = {fst = ver.s ; snd = obj.s} ;
Scramble _ vp = {fst = vp.snd ; snd = vp.fst } ;
To_NP _ n = {s = n.s} ;
Bob = {s = "Bob"} ;
Loved = {s = "loved"} ; -- just add rest of the lexicon
Include, Exclude = {s = ""} ; -- these need to have a linearisation, otherwise nothing works!
2. 使用语法的提示和技巧
使用 Bob 和 Loved 的词典,它只生成一棵树。当我使用命令gt
(generate_trees)时,如果我不给出类别标志,它会自动使用起始类别,即S。
> gt | l -treebank
To_S (Pred Include (To_NP Include Bob) (Compl Include Loved (To_NP Include Bob)))
Bob loved Bob
在 S 中解析也可以:
p "Bob loved Bob"
To_S (Pred Include (To_NP Include Bob) (Compl Include Loved (To_NP Include Bob)))
解析VP:添加一个linref
使用当前的语法,我们无法解析任何 VP:
> p -cat="VP ?" "Bob loved"
The parser failed at token 2: "loved"
> p -cat="VP ?" "loved Bob"
The parser failed at token 2: "Bob"
那是因为 VP 的 lincat 是不连续的并且没有单个s
字段。但是,如果您还想解析 VP,您可以为 VP 的类别添加一个linref,如下所示:
linref
VP = \vp -> vp.fst ++ vp.snd ;
现在它甚至可以解析 VP,类似于:
Cl> p -cat="VP ?" "loved Bob"
Compl ?3 Loved (To_NP ?3 Bob)
这些奇怪的问号是元变量,我们接下来会修复它。
元变量
如果你读过我的博客,你可能已经看过关于元变量的一些内容:https ://inariksit.github.io/gf/2018/08/28/gf-gotchas.html#metavariables-or-those-question-解析时出现的标记
GF 有一个奇怪的要求,即每个参数都需要提供一个字符串(甚至是一个空字符串!),否则解析时无法识别。即使没有歧义,也会发生这种情况。
我们已经对空字符串进行了线性Include
化Exclude
——它们在任何情况下都必须进行一些线性化,否则整个语法都不起作用。所以我们需要将Clude
' 的空字符串添加到所有的线性化中,否则 GF 解析器会被混淆。
在您刚刚Clude
用下划线标记参数的所有线性化中,我们现在就这样做。不管我们将它添加到哪个字段,它只是一个空字符串,对输出没有任何影响。
Pred _clu sub vp = {s = sub.s ++ vp.fst ++ vp.snd ++ _clu.s} ;
Compl _clu ver obj = {fst = ver.s ; snd = obj.s ++ _clu.s} ;
Scramble _clu vp = {fst = vp.snd ; snd = vp.fst ++ _clu.s} ;
To_NP _clu n = {s = n.s ++ _clu.s} ;
在此之后,VP 中的解析工作没有问号:
Cl> p -cat="VP ?" "loved Bob"
Compl Exclude Loved (To_NP Exclude Bob)
Compl Include Loved (To_NP Include Bob)
Cl> p -cat="VP ?" "Bob loved"
Scramble Exclude (Compl Exclude Loved (To_NP Exclude Bob))
我还在获取元变量????
gt
所以我们修复了它,但是当我得到一棵使用的树时,为什么我仍然会得到问号Scramble
?
> gt -cat="VP Exclude"
Compl Exclude Loved (To_NP Exclude Bob)
Scramble ?3 (Compl ?3 Loved (To_NP ?3 Bob))
Scramble Exclude (Scramble ?4 (Compl ?4 Loved (To_NP ?4 Bob)))
> gt -cat="VP Include"
Compl Include Loved (To_NP Include Bob)
那是因为该Scramble
操作真正压制了它的论点。这不仅仅是 GF 编译器愚蠢并拒绝合作的情况,即使很明显存在哪个参数,在这种情况下,无法检索它是哪个参数:Scramble 将所有内容都变成VP Exclude
.
添加 linref 并包含 Clude 的空字符串后的完整语法
只是为了完整起见,这里是所有的变化。
lin
To_S pr = pr ;
Pred _clu sub vp = {s = sub.s ++ vp.fst ++ vp.snd ++ _clu.s} ;
Compl _clu ver obj = {fst = ver.s ; snd = obj.s ++ _clu.s} ;
Scramble _clu vp = {fst = vp.snd ; snd = vp.fst ++ _clu.s} ;
To_NP _clu n = {s = n.s ++ _clu.s} ;
Bob = {s = "Bob"} ;
Loved = {s = "loved"} ;
Include, Exclude = {s = ""} ;
linref
VP = \vp -> vp.fst ++ vp.snd ;
3. 在没有依赖类型的情况下制作此语法的替代方法
如果您想使用 Python 中的语法或 C 运行时的 Haskell 绑定,那么您很不幸:C 运行时不支持依赖类型。因此,这是您的语法的一个版本,我们在其中使用具体语法和nonExist
令牌中的参数来模仿行为(请参阅https://inariksit.github.io/gf/2018/08/28/gf-gotchas.html#raise-an -异常)。
我将文档保持在最小限度(因为这个答案已经很长了),但是如果您对此解决方案的某些部分有任何疑问,请问!
抽象语法
cat
S ; VP ; NP ; V2 ; N ;
fun
Pred : NP -> VP -> S ;
Compl : V2 -> NP -> VP ;
Scramble : VP -> VP ;
To_NPIncl,
ToNPExcl : N -> NP ;
Bob, Billy, John, Mary, Lisa : N ;
Liked, Loved, Hated : V2 ;
具体语法
param
Clude = Include | Exclude ;
lincat
-- To mimic your categories VP Clude and NP Clude,
-- we add a parameter in VP and NP
VP = {fst, snd : Str ; clude : Clude} ;
NP = {s : Str ; clude : Clude} ;
-- The rest are like in your grammar
S, V2, N = {s : Str} ;
lin
-- No need for Pre_S, we can match NP's and VP's Clude in Pred
-- Only make a sentence if both's Clude is Include
Pred np vp = case <np.clude, vp.clude> of {
<Include,Include> => {s = np.s ++ vp.fst ++ vp.snd} ;
_ => {s = Predef.nonExist}
} ;
-- As per your grammar, V2 has no inherent Clude, but NP does
Compl v2 np = {
fst = v2.s ;
snd = np.s ;
clude = np.clude ;
} ;
-- Scramble doesn't look at it's argument VP's clude,
-- just makes it into Exclude automatically.
Scramble vp = {
fst = vp.snd ;
snd = vp.fst ;
clude = Exclude ;
} ;
-- Your grammar has the function To_NP : (c : Clude) -> N -> NP c ;
-- We translate it into two functions.
To_NPIncl n = n ** {clude = Include} ;
To_NPExcl n = n ** {clude = Exclude} ;
-- Finally, lexicon.
Bob = {s = "Bob"} ;
Loved = {s = "loved"} ;
现在,当我们生成 S 类中的所有树时,有一个使用 Scramble,但它没有线性化。
> gt | l -treebank
ClParam: Pred (To_NPIncl Bob) (Compl Loved (To_NPIncl Bob))
ClParamEng: Bob loved Bob
ClParam: Pred (To_NPIncl Bob) (Scramble (Compl Loved (To_NPIncl Bob)))
ClParamEng:
也许比你的版本不那么优雅,甚至没有生成树,但这只是为了演示不同的方法。如果您正在使用 Haskell 并且不需要使用 C 运行时,请随时继续您的方法!