Haskell 越界`select`即使我`constraint`索引

Haskell 越界`select`即使我`constraint`索引,haskell,smt,sbv,symbolic-execution,Haskell,Smt,Sbv,Symbolic Execution,我有一个值的静态长度列表ks::[SInt16]和一个索引x::SInt16。我想使用x索引到列表中: (.!) :: (Mergeable a) => [a] -> SInt16 -> a xs .! i = select xs (error "(.!) : out of bounds") i 我希望能够将(.!)与足够约束的x一起使用,如下所示: sat $ do let ks = [1, 3, 5, 2, 4] x &l

我有一个值的静态长度列表
ks::[SInt16]
和一个索引
x::SInt16
。我想使用
x
索引到列表中:

(.!) :: (Mergeable a) => [a] -> SInt16 -> a
xs .! i = select xs (error "(.!) : out of bounds") i
我希望能够将
(.!)
与足够约束的
x
一起使用,如下所示:

sat $ do
    let ks = [1, 3, 5, 2, 4]      

    x <- sInt16 "x"
    constrain $ 0 .<= x .&& x .< literal (fromIntegral $ length ks)

    let y = ks .! x
    return $ y .< x
sat$do
设ks=[1,3,5,2,4]
x简单解
select
在符号执行期间由SBV完全展开,因此您必须提供适当的默认值,正如您所发现的那样。因此,如果您确实想使用
选择
,您必须在那里找到一个实际值

为了满足您的迫切需要,我建议您只需定义:

(.!):(可合并a)=>[a]->SInt16->a
[]       .! _ = 错误“(.!):空列表!”
(x:)!。!i=选择xs x i
只要您确保在
i
上声明了足够的约束,这应该可以正常工作

稍微好一点的方法 以上要求用户跟踪索引变量的适当约束,这可能会变得相当棘手。在这些情况下使用的一个简单技巧是使用“智能”构造函数。首先定义:

import Data.SBV
mkIndex::SIntegral b=>String->[a]->Symbolic(SBV b)
MKDINDEX nm lst=我是否在16->a
[]       .! _ = 错误“(.!):空列表!”
(x:)!。!i=选择xs x i
现在你可以说:

p=sat$do让ks=[1,3,5,2,4]
x字符串->符号(索引a b)
mkIndex nm=do def索引a b->SBV a
xs。!索引(i,i')=选择xsi'
现在假设您尝试执行
sat
,但在索引上设置了不正确的约束:

p=sat$do让ks=[1,3,5,2,4]
xi@(Index(x))::Index Int16 Int16 10
让y=ks。!席
纯$y。
您将获得:

*Main> p
Satisfiable. Model:
  x_access_out_of_bounds_value =     0 :: Int16
  x                            = 16386 :: Int16
通过这种方式,您可以看到出现了问题,以及解算器选择了什么值来满足访问超出边界的情况

总结 你采取哪种方法取决于你的实际需要。但如果可能的话,我建议至少使用第二种方法,因为SMT解算器总是可以“聪明地”选择值,从而为您提供意外的模型。这样至少可以防止最明显的bug。在生产系统中,我坚持使用第三种方法,因为调试由复杂约束引起的bug在实践中可能相当困难。你留给自己的“跟踪”变量越多越好