代码之家  ›  专栏  ›  技术社区  ›  Tarciana

如何在Alloy中构建递归谓词/函数

  •  2
  • Tarciana  · 技术社区  · 9 年前

    我试图在Alloy中生成两组类,例如,重构之前的类 重构应用程序后的应用程序和类。 假设在第一个集合中,我们有以下类:

    ALeft -> BLeft -> CLeft    
                      Class1
                      Class2 -> Class3
                             -> Class4
    

    意味着ALeft是BLeft的父级,而BLeft又是CLeft、Class1和 Class2,它又是Class3和Class4的父级。

    另一方面,根据相同的推理,我们在第二组中 以下类别组:

    ARight -> BRight -> CRight
                        Class1'
                        Class2' -> Class3'
                                -> Class4'
    

    由于每组代表 相同的类别,但按不同的时间顺序(不同的状态),必须保证相应的 等价物,例如Class1和Class1'是等价的,这意味着它们具有相同的字段、方法等(考虑到重构只发生 对于相同的推理,Class2和Class2’、Class3和Class3’、Class4和Class4’也是等效的。此外,我们应该在Left和Right类中的方法之间具有等价性。例如,如果我们有一个Left类方法,比如:

    public int leftClassMethod(){
        new ALeft().other();
    }
    

    然后,必须有相应的Right类方法,如:

    public int rightClassMethod(){
        new ARight().other();
    }
    

    正如Loic(在这个讨论列表中)所建议的,这些类的等价性开始得到保证,但我们必须补充下面的谓词类AreTheSame,以便也保证它们方法的等价性。考虑以下模型:

    abstract sig Id {}
    
    sig ClassId, MethodId,FieldId extends Id {}
    
    one sig public, private_, protected extends Accessibility {}
    
    abstract sig Type {}
    
    abstract sig PrimitiveType extends Type {}
    
    one sig Long_, Int_ extends PrimitiveType {}
    
    sig Class extends Type {
        id: one ClassId,
        extend: lone Class,
        methods: set Method,
        fields: set Field,
    }
    
    sig Method {
        id : one MethodId,
        param: lone Type,
        return: one Type,
        acc: lone Accessibility,
        b: one Block
    }
    
    sig Block {
        statements: one SequentialComposition
    }
    
    sig SequentialComposition {
        first: one StatementExpression,
        rest: lone SequentialComposition
    }
    
    abstract sig Expression {}
    abstract sig StatementExpression extends Expression {}
    
    sig MethodInvocation extends StatementExpression{
        pExp: lone PrimaryExpression, 
        id_methodInvoked: one Method
    }
    
    sig AssignmentExpression extends StatementExpression {
        pExpressionLeft: one FieldAccess,
        pExpressionRight: one {Expression - newCreator - VoidMethodInvocation - PrimaryExpression - AssignmentExpression }
    }
    
    abstract sig PrimaryExpression extends Expression {}
    
    sig this_, super_ extends PrimaryExpression {}
    
    sig newCreator extends PrimaryExpression {
        id_cf : one Class
    }
    
    sig FieldAccess {
        pExp: one PrimaryExpression,
        id_fieldInvoked: one Field
    }
    
    sig Left,Right extends Class{}
    
    one sig ARight, BRight, CRight extends Right{}
    
    one sig ALeft, BLeft, CLeft extends Left{}
    
    pred law6RightToLeft[] {
        twoClassesDeclarationInHierarchy[]
    }
    
    pred twoClassesDeclarationInHierarchy[] {
         no disj x,y:Right | x.id=y.id
         Right.*extend & Left.*extend=none
         one r: Right | r.extend= none
         one l:Left| l.extend=none
    
         ARight.extend=none
         ALeft.extend=none
         BRight in CRight.extend
         BLeft in CLeft.extend
         ARight in BRight.extend
         ALeft in BLeft.extend
         #(extend.BRight) > 2
         #(extend.BLeft) > 2
         #(extend.ARight) = 1
         #(extend.ALeft) = 1
         CLeft.id=CRight.id
    
         all m:Method | m in ((*extend).ALeft).methods => m !in ((*extend).ARight).methods
         all m:Method | m in ((*extend).ARight).methods => m !in ((*extend).ALeft).methods
         some Method
         all r:Right | all l:Left|  (r.extend= none and l.extend=none) implies classesAreTheSameAndSoAreTheirCorrespondingSons[r,l]
    }
    
    pred classesAreTheSameAndSoAreTheirCorrespondingSons[right,left: Class]{
        classesAreTheSame[right,left]
        all r: right.^~extend | one l :left.^~extend | classesAreTheSame[r,l] and classesAreTheSame[r.extend ,l.extend]
        all l:left.^~extend | one r :right.^~extend | classesAreTheSame[r,l] and  classesAreTheSame[r.extend ,l.extend]
    }
    
    pred classesAreTheSame[r,l: Class]{
        r.id=l.id
        r.fields=l.fields
    
        #r.methods = #l.methods
        all mr: r.methods | one ml: l.methods | mr.id = ml.id && mr.b != ml.b 
        all mr: l.methods | one ml: r.methods | mr.id = ml.id && mr.b != ml.b 
    
        all r1: r.methods, r2: l.methods | r1.id = r2.id => 
            equalsSeqComposition[r1.b.statements, r2.b.statements]
    }
    
    pred equalsSeqComposition[sc1, sc2: SequentialComposition]{
        equalsStatementExpression[sc1.first, sc2.first] 
        //#sc1.(*rest) = #sc2.(*rest)
    }
    
    pred equalsStatementExpression [s1, s2: StatementExpression]{
        s1 in AssignmentExpression => (s2 in AssignmentExpression && equalsAssignment[s1, s2])
        s1 in MethodInvocation => (s2 in MethodInvocation && equalsMethodInvocation[s1, s2])
        s1 in VoidMethodInvocation => (s2 in VoidMethodInvocation && equalsVoidMethodInvocation[s1, s2])
    }
    
    pred equalsAssignment [ae, ae2:AssignmentExpression]{
        equalsPrimaryExpression[(ae.pExpressionLeft).pExp, (ae2.pExpressionLeft).pExp]
        equalsPExpressionRight[ae.pExpressionRight, ae2.pExpressionRight]
    }
    
    pred equalsPrimaryExpression[p1, p2:PrimaryExpression]{
        p1 in newCreator  => p2 in newCreator && equalsClassesId [p1.id_cf, p2.id_cf]
        p1 in this_ => p2 in this_ 
        p1 in super_ => p2 in super_
    }
    
    pred equalsPExpressionRight[e1, e2:Expression]{
        e1 in LiteralValue => e2 in LiteralValue 
        e1 in MethodInvocation => e2 in MethodInvocation && equalsMethodInvocation[e1, e2]
    }
    
    pred equalsMethodInvocation[m1, m2:MethodInvocation]{
        equalsPrimaryExpression[m1.pExp, m2.pExp]
        m1.id_methodInvoked.id = m2.id_methodInvoked.id 
        m1.param = m2.param
    }
    
    pred equalsVoidMethodInvocation[m1, m2:VoidMethodInvocation]{
        equalsPrimaryExpression[m1.pExp, m2.pExp]
        m1.id_voidMethodInvoked.id = m2.id_voidMethodInvoked.id
        m1.param = m2.param
    }
    
    run law6RightToLeft for 10 but 17 Id, 17 Type, 17 Class
    

    我的想法是通过它们的id来识别相应的方法(leftClassMethod()和rightClassMethod(())(根据模型,这是保证相同的)。然而,谓词equalsSeqComposition不起作用,当我尝试将签名SequentialComposition的其余关系包括在比较中时,情况会变得更糟(上面在谓词equalSeqComposition中进行了评论)。最后一个比较更加困难,因为Alloy不允许递归,并且当您使用传递闭包时,会丢失与排序相同的继承级别。知道我怎么用Alloy来表示这个吗?

    1 回复  |  直到 9 年前
        1
  •  4
  •   Community George Stocker    7 年前

    只有当递归深度未超过3时,才能在Alloy中递归调用函数和谓词。请参阅: Programming recursive functions in alloy

    对于您的问题,您可以使用传递闭包运算符模拟您试图指定的递归。

    我会重写你的谓词 classesAreTheSameAndSoAreTheirCorrespondingSons 如下所示:

    pred classesAreTheSameAndSoAreTheirCorrespondingSons[right,left: Class]{
        classesAreTheSame[right,left]
        all r: right.^~extend | one l :left.^~extend | classesAreTheSame[r,l] and classesAreTheSame[r.extend ,l.extend]
        all l:left.^~extend | one r :right.^~extend | classesAreTheSame[r,l] and  classesAreTheSame[r.extend ,l.extend]
    }
    

    此谓词强制要求给定的两个类right和left是相同的,并且任何继承(直接或间接)right/left的类r/l在继承(直接或者间接)left/right的类l/r中都有一个计数器部分。

    支票 classesAreTheSame[r.extend ,l.extend] 用于检查r和l在使用传递闭包时是否处于相同的继承级别,因为排序丢失。

    这是我设计的解决您问题的小模型:

    abstract sig Class {
        id: Int,
        extend: lone Class
    }{
       this not in this.^@extend
    }
    
    sig Left,Right extends Class{}
    
    fact{
         no disj x,y:Right | x.id=y.id
         Right.*extend & Left.*extend=none 
         one r: Right | r.extend= none 
         one l:Left| l.extend=none
         all r:Right | all l:Left|  (r.extend= none and l.extend=none) implies classesAreTheSameAndSoAreTheirCorrespondingSons[r,l]    
    }
    
    pred classesAreTheSameAndSoAreTheirCorrespondingSons[right,left: Class]{
        classesAreTheSame[right,left]
        all r: right.^~extend | one l :left.^~extend | classesAreTheSame[r,l] and classesAreTheSame[r.extend ,l.extend]
        all l:left.^~extend | one r :right.^~extend | classesAreTheSame[r,l] and  classesAreTheSame[r.extend ,l.extend]
    }
    
    pred classesAreTheSame[r,l: Class]{
        r.id=l.id
    }
    run {} for exactly 10 Class
    

    祝其余人好运;)