我正在尝试在Idris中建模一个简单的调查表,目前正在努力验证用户输入,这是一个字符串,w.r.t。问的类型。
目前我有以下类型:
data Question : Type where
QCM : {numOptions : Nat}
-> (question : String)
-> (qcmOptions : Vect numOptions String)
-> (expected : Fin numOptions)
-> Question
data Answer : (q : Question) -> Type where
AnswerQCM : (option : Fin n) -> Answer (QCM {numOptions = n } q opts exp)
total
isCorrectAnswer : (q : Question ) -> Answer q -> Bool
isCorrectAnswer (QCM {numOptions} question qcmOptions expected) (AnswerQCM option) = option == expected
data IsAnswer : (s : String) -> (q : Question) -> Type where
ValidQCM : (option : Fin n) -> IsAnswer s (QCM {numOptions = n } q opts exp)
notInRange : (num : Fin n) -> { auto prf : GT numOptions n }
-> IsAnswer s (QCM {numOptions} q opts exp) -> Void
notInRange num x = ?notInRange_rhs
我没有看到如何编写notInRange
函数,该函数应该证明某些数字可能不是多项选择问题调查的有效答案:此数字应该在正确的范围内问题中提供的选项数量。
更一般地说,我想编写一个看似如下的readAnswer
函数:
readAnswer : (s : String) -> (q : Question) -> Dec (IsAnswer s q)
readAnswer s (QCM question qcmOptions expected) = ?readAnswer_rhs_1
我似乎很难找到contra
的{{1}}部分,因为我的类型没有正确地表达我想要的限制,以至于其中一些可以被证明无人居住。
答案 0 :(得分:4)
嗯,回答你的问题非常重要。你有几个设计问题。我不会只粘贴结果代码。 Intead我会尝试解释当前的方法发生了什么以及存在什么问题。也许我的解决方案并不完全符合你的要求(也许你甚至不需要你想要的东西),但我希望它对你有帮助。
一个大问题是你的isCorrectAnswer
功能。它返回Bool
并且Bool
对于证明是不好的,因为编译器对Bool
的值知之甚少。如果您需要更强的证明能力,则应使用Refl
代替True/False
。与Maybe
和Dec
相同的情况。如果您已经足够Maybe
那么就可以了,但如果您希望您的实施可以证明您应该使用Dec
而不是Maybe
。一旦你决定在你的函数中使用Dec
,你正在使用和调用它的所有函数也应该包含更可证明的信息。
思考过程的良好开端是考虑具有以下功能:
isCorrectAnswer : (q: Question) -> (a: Answer q) -> expected q = option a
不幸的是,由于两个问题,这个功能不可能存在。
Question
和Answer
,那么预期的答案与给定的完全一致并不总是正确的。因此,您应该将此函数转换为具有此类属性的数据类型。这只是一种通用方法。expected
数据类型编写Question
函数。也许有人可以,但我尝试过但失败了。此expected
函数不是当前实现中相应字段的名称。而numOptions
是Question
的内部事物。没有模式匹配,从外面看不到它。因此,在矢量长度上参数化Question
是件好事。要解决2,我将以下一种方式稍微转换您的数据类型:
record Question (numOptions : Nat) where
constructor QCM
question : String
qcmOptions : Vect numOptions String
expected : Fin numOptions
record Answer (q : Question n) where
constructor AnswerQCM
option : Fin n
所以现在属性看起来像这样:
data IsCorrectAnswer : (q : Question n) -> (a: Answer q) -> Type where
IsCorrect : {q: Question n}
-> {a: Answer q}
-> expected q = option a
-> IsCorrectAnswer q a
这是一个简单的决定程序:
isCorrectAnswer : (q : Question n) -> (a: Answer q) -> Dec (IsCorrectAnswer q a)
isCorrectAnswer (QCM _ _ expected) (AnswerQCM option) =
case decEq expected option of
Yes prf => Yes (IsCorrect prf)
No contra => No (\(IsCorrect prf) => contra prf)
在我的问题中,当我在String
和Question
之间实施时,您需要Answer
和Question
之间的属性。所以现在理论上你可以在qcmOptions
中查找此字符串,查找Fin
索引,将其转换为答案并获取Dec
IsCorrectAnswer
。好吧,有点。除非事实证明非常非常重要。只是因为你不能证明你想要的定理的原因。
在您的数据类型中,{strong} s: String
与(option : Fin n)
之间存在无。可能有几个额外的属性和数据类型可以提供帮助。但更简单的解决方案是使用IsAnswer
Vect
使isCorrectAnswer
属性包含更多信息。这里的最终实现与data IsAnswer : (s : String) -> (q : Question n) -> Type where
ValidQCM : {q: Question n} -> Elem s (qcmOptions q) -> IsAnswer s q
readAnswer : (s : String) -> (q: Question n) -> Dec (IsAnswer s q)
readAnswer s (QCM _ options _) = case isElem s options of
Yes prf => Yes (ValidQCM prf)
No contra => No (\(ValidQCM prf) => contra prf)
几乎相同:
isCorrectAnswer
你可以注意到我在这里没有使用pip uninstall pywin32
而我可能不会因为我之前提到的问题而使用pip install <Name of the wheel file with extension>
。但我不需要在这里使用它。更简单的解决方案更好。
P.S。对不起,我可能会写一些简短的答案,但我希望通过这种方式,这个答案可以向你澄清更多的事情。