没有合适的资源?快使用搜索试试~ 我知道了~
理论计算机科学电子笔记147(2006)5-30www.elsevier.com/locate/entcs将面向时间的规划与战略规划相Karl Trygve Kalleberg1卑尔根大学信息学系P.O. Box 7800,N-5020 BERGEN,Norway乌得勒支大学信息与计算科学研究所P.O. Box 80089,3508 TB乌得勒支,荷兰Eelco Visser2乌得勒支大学信息与计算科学研究所P.O. Box 80089,3508 TB乌得勒支,荷兰摘要诸如日志记录、持久性、调试、跟踪、分发、性能监视和异常处理之类的属性出现在大多数编程范例中,并且通常很难甚至不可能用传统的模块化机制进行模块化,因为它们是横切的。最近,面向方面编程作为分离这些关注点的实用解决方案而受到认可。在本文中,我们描述了一个扩展的重写语言,捕获这些属性的术语。我们展示了我们的方面语言操作员一个简洁,实用和适应性强的解决方案,用于处理前向数据流传播和动态类型检查项的意外算法扩展。我们简要讨论了在基于规则的术语重写系统中设计和实现方面扩展时所面临的一些挑战。关键词:面向方面编程;语言扩展;基于规则的编程;非预期扩展;策略编程1介绍良好的模块化是软件设计、开发和维护的关键。我们希望我们的软件结构接近我们想要的思考1 电子邮件地址:karltk@ii.uib.no2 电子邮件地址:visser@cs.uu.nl1571-0661 © 2006 Elsevier B. V.在CC BY-NC-ND许可下开放访问。doi:10.1016/j.entcs.2005.06.0356K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5关于它[21],通过干净地将问题域的属性分解为基本功能单元或组件。这些可以直接映射到语言结构,如数据类型和函数。并不是问题的所有属性都容易分解成组件,而是变成非功能性的,并且经常横切我们的模块结构。这些属性称为方面。面向方面的软件开发[13]的目标是将这些横切关注点模块化。通过使方面成为编程语言的一部分,我们在模块化软件时具有更大的灵活性,因为横切属性不再需要分散在组件中。使用方面,这些属性现在可以完全在单独的单元中声明,每个属性一个。一般方面的示例包括安全性、日志记录、持久性、调试、跟踪、分发、性能监视、异常处理、起源跟踪和可追溯性。所有这些都发生在基于规则的编程环境中,除了一些特定于域的,例如使用布局重写。现有的文献主要讨论面向对象语言的这些问题的基于方面的解决方案,而基于规则语言的特定于范例的问题和部署的解决方案的文档很少。在本文中,我们描述了方面的设计和使用基于规则的编程。我们介绍了Askelgo语言的声明单独的,横切关注点,并讨论了连接点模型,它是建立。我们展示了它的实际用途,并通过三个小的案例研究不断传播的问题,突出了它的一些实施细节。本文的贡献包括:(i) 为基于规则的编程语言实现的方面语言扩展的描述。(ii) 一个例子,它适用于以精确和简洁的方式添加灵活的动态类型检查。(iii) 通过展示如何使用侵入式软件组合将恒定传播策略推广为通用的自适应前向传播方案,演示其在意外算法扩展中的应用[2]。我们按以下步骤进行。在下一节中,我们将通过示例描述用于术语重写的Peggo语言。在第3节中,我们将介绍一个扩展,允许单独声明横切关注点,并展示此扩展如何促进声明性代码组合。在第4节中,我们讨论了方面扩展非常有用的三种情况:日志记录、术语的类型检查和算法自适应。在第6节中,我们讨论了以前的,相关的和未来的工作。K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)572基本原理为了讨论基于规则的编程的各个方面,我们将简要介绍用于术语重写的Ringgo语言。熟悉该语言的读者可能想跳到第2.2节。2.1西班牙语Replego是基于重写策略的概念;用于相对于一组重写规则转换术语的算法。在大多数重写引擎中,这些策略是固定的,并且通常要求规则集是连续的和终止的。在Ringgo中,策略是用户定义的,因此为用户提供了对规则选择和应用顺序的细粒度控制。虽然一个术语可以代表任何东西,但共同命名为Xplugo/XT的Xplugo库和工具链面向计算机语言和程序转换。2.1.1签名和条款签名描述了一阶项的结构 它由一组构造函数组成,每个构造函数接受零个或多个参数。签名S上的项是通过构造器的应用从其签名生成的语法表达式。例如,Assign(Var(“x”),Plus(Int(“1”),Var(“x”)它表示x=1+x的值。2.1.2匹配和构建Aplogo是围绕两个基本操作符match和build构建的。写,写!,用于从构造函数和原始排序构造一个术语。匹配,写?用于将模式与术语进行匹配。模式是一个可能包含变量的术语。如果术语模式中的变量已经绑定到术语,则模式中的变量将被替换为值如果模式中的变量尚未绑定,则这些变量将绑定到模式所绑定到的当前项中的实际值应用。构建和匹配始终针对隐式当前项进行。 假设图1中的签名并且x绑定到“0”,y未绑定,下面的代码片段将首先用加号term,然后匹配新的当前term并将y绑定到“2”:!int n = Intn(x);?加(,Int(y)这里的下划线()是一个空格。它匹配任何术语并忽略它。8K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)52.1.3重写规则重写规则描述了从一个术语到另一个术语的转换,并且可能由条件保护。它的形式为R:l-> r,其中R是规则名称,l是左侧模式,r是右侧模式。条件可以添加到where-子句中,该子句包含一个策略表达式,如下所述左手边的图案与如果匹配成功,则实例化右侧模式以构造新术语。多个规则可以具有相同的名称,并且规则始终按名称调用。当存在多个同名规则时,将尝试所有规则,直到有一个匹配,并返回其结果。如果多个规则可以匹配,则只有一个规则会成功。语言语义学并没有指定是哪个。在图1中,EvalBinOp是具有条件的重写规则的示例。重写具有动态(相对于词法)范围的规则称为动态重写统治作用域完全由程序员通过在策略表达式中插入特殊的作用域结构来控制[20]。 在图1中,规则(PropConst:...... )是动态规则PropConst的声明的示例,并且规则(PropConst:-. )显示其删除。2.1.4重写策略重写策略是用于转换项的算法。如果成功,则结果是转换后的项。如果失败了,就没有结果。策略控制规则或其他策略的应用顺序。它们可以使用一组内置的策略组合子组合成策略表达式,例如顺序组合(;)和确定性选择(<+)。该语言已两个基本策略:id将始终成功并返回标识项而失败永远都是失败图1中的prop-const-assign是策略定义的一个例子。2.1.5规则和策略参数策略和规则定义可以包含两种参数,高阶策略参数和期限参数。高阶策略参数的工作方式与函数式语言中的高阶函数类似。术语参数用于将上下文信息传递到规则或策略中,涉及当前任期。图3中对log的调用是一个只有术语参数的策略调用战 略 论 点 是 从 一 个 术 语 “ 战 略 ” 中 分 离 出 来 的 。 |. 一 个 rgumentsbeforethe|被认为是策略参数,之后的参数被认为是术语参数。在没有术语参数的情况下,|省略了。K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)59模块prop-const签名构造函数变量:Id ->变量:Var -> ExpInt:String ->Exp加:Exp * Exp -> Exp如果:Exp * Exp * Exp-> Exp而:Exp * Exp -> Exp分配:Var * Exp-> Exp规则EvalBinOp: Plus(Int(i),Int(j))-> Int(k),其中addS>(i,j)=>kEvalIf: If(Int(“0”),e1,e2)->e2策略prop-const =PropConst + prop-const-assign + prop-const-if<+ prop-const-while+(all(prop-const);try(EvalBinOp))prop-const-assign =Assign(?Var(x),prop-const =>e); if is-value>e then rules(PropConst:Var(x)->e)else rules(PropConst:- Var(x))endprop-const-if =If(prop-const,id);(EvalIf; prop-const +(If(id,prop-const,id)/PropConst\ If(id,id,prop-const)prop-const-while =?While(e1,e2);(While(prop-const,id); EvalWhile<+(/PropConst\*While(prop-const,prop-const)Fig. 1.这是一个为小型命令式语言定义过程内条件常量传播转换策略的程序的摘录。2.1.6Term遍历术语遍历策略在术语的整个结构中应用重写策略该库为此提供了大量原始策略 要将策略s应用于项的所有直接子项,可以写all(s)。 其他所提供的遍历的示例是自底向上和自顶向下。 一个替代术语遍历的机制是使用同余运算符。 对于每个n-10K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5⇒图二.显示常量传播策略的示例。有 一 个 对 应 的 同 余 算 子 定 义 了一个具有相同名字和数组的策略, C(s1,. ,sn)。 当应用于项C(t1,. ,tn)的策略将s1应用于t1,s2应用于t2,然后继续,从而产生项C(t,J,. ,tJ),提供了所有成功的策略。如果我失败了1N策略C。图1中的策略prop-const-if中的If(prop-const,id,id)是If的同余的示例。2.1.7模块化模块是RIGGO程序结构的粗元素。一个模块是一个以声明模块名开始的文件。下面是章节,每个章节都有一个章节标题,其中的签名,规则和策略与我们有关。构造函数声明必须出现在签名节的内部规则和策略可以在规则和策略部分自由混合,但好的形式是根据其类型来定义。2.2常数传播图1中的代码显示了带有赋值、While和If结构的命令式语言的常量传播策略。常数传播算法的原理很简单:只要有一个常数到一个变量的赋值,我们就使用动态重写来统治如果该变量随后被了一个非常量值,则该规则将被删除。这在prop-const-assign中完成。 我们使用动态重写规则将每个常量变量替换为它的值(如果已知)。 这为基本求值规则EvalBinOp和EvalIf打开了一扇大门,以简化Plus和If上的表达式。图中给出了其应用的一个例子二、prop-const策略是顶级驱动策略,负责递归地将常数传播应用于项。 它通过调用规则PropConst来工作,这将用它的常量替换任何我们知道值的变量项。事实上,PropConst是一组同名的动态规则由于PropConst是动态的,因此随着遍历的进行,规则可以被添加到集合中或从集合这在prop-const-assign策略中完成。正如我们在prop-const中看到的,prop-const-assign在PropConst失败的任何项上被调用,即 当我们不能用常量替换变量时。如果调用prop-const-assign,x:=2; y:=1x:= 1 +1如果x那么y:= 1else y:= 0fiK.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)511在一个Assign术语上,并且它被分配了一个常量值,我们生成一个名为PropConst的新规则,从而将其添加到PropConst规则集。新生成的规则是从变量(Var)到常量值的查找。当应用时,新的PropConst规则会将该变量的出现重写为其关联值。回到prop-const策略。如果当前项不是我们知道常数值的Var,也不是我们可以选择常数值形式的Assign,prop-const将尝试其他策略,forIf(prop-const-if)和While(prop-const-while)。如果都失败了,prop-const将回退到递归地将自身应用于当前项的所有子项,然后在结果上尝试EvalBinOpprop-const-if策略将使用同余运算符匹配If构造,同时将prop-const应用于条件表达式。如果同余成功,则prop-const-if策略通过以下两种方式进行:(1)使用EvalIf规则简化If,然后递归地在结果上继续prop-const算法,或者(2)依次将prop-const递归地应用于then-branch和else-branch,并仅保留在两个分支之后有效的PropConst规则,即在两个分支中定义和相等的规则。动态规则交集运算符s1 /PropConst\s2将两个策略s1然后s2依次应用于当前项,同时将动态规则PropConst的相同规则集分发(克隆)给两个策略。之后,只有那些在两个分支中一致的规则才被保留。类似的解释也适用于prop-const-while,其中定点运算符使用/PropConst\* s。该操作符将重复应用s,直到获得稳定的规则集。每次迭代将s应用于原始项,最终迭代的结果作为新项保留。2.2.1泛化与适应正如所写的那样,该算法已经有了一些变化点,用户可以使用我们提供的语言功能对其进行扩展,而无需修改算法本身。例如,为EvalIf添加另一个处理非零常量的求值规则。但是,我们也可能希望将其他扩展和调整应用于该算法。第4.1节显示了如何我们可以用日志功能来扩展它,以记录所有规则调用,在4.2节中,我们展示了用术语的动态类型检查来扩展它以确保结果是正确的项。 最后,在4.3节中,我们将展示如何将算法重构为一个更通用的模式,用于前向传播数据流转换。所有的扩展和调整都是在我们对Applogo语言的方面扩展的帮助下进行的,12K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5模块prop-const-logger导入日志记录prop-const方面pointcut call = strategies(prop-const)aspect prop-const-logger =在此之前:call = log(|(“Replanking constantpropagator”)图3.第三章。使用日志记录扩展常量传播模块的方面日志来自伐木模块,是Moogo库的一部分。接下来描述3阿维尼翁戈ApplaGo是ApplaGo语言的一个扩展,它以模块化的方式解决了声明横切关注点的问题。该语言的扩展与AZJ语言有一些相似之处[12]。我们试图保留大部分术语,以及它的连接点模型的一些属性,尽管后者已经被调整以更好地适应基于规则的重写系统的范式。类似于ApplyJ,我们为程序合并器提供了称为切入点的表达式(我们的是涉及谓词而不是集合理论的布尔表达式),用于在程序执行中挑选出定义良好的点,称为连接点。切入点在广告中用于精确定位插入代码的位置,在之前、之后或周围。插入的代码被声明为通知的一部分。建议又被收集在称为方面的命名实体中。将程序与其方面组合在一起的行为称为编织。图3显示了我们如何使用一个方面来扩展常量传播器和平凡日志记录。在下面的部分中,我们将给出一个更高级的日志记录示例。在这里,我们将介绍本示例中引入的新语言特性。3.1连接点连接点是程序执行中定义良好的点,控制流通过它两次:一次是在进入连接点标识的子计算的路上,一次是在出去的路上。方面语言的目的是允许程序员精确而简洁地识别和操作连接点。K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)513Joinpoint匹配调用(name-expr=>n)策略(name-expr=>n)规则(name-expr=>n)匹配(pattern=>t)构建(pattern=>t)失败策略或规则调用策略执行规 则 执 行 模 式匹配术语构造fail的显式调用连接点上下文匹配withincode(name-expr =>n)matchflow(name-regex => t)args(n0,n1,. ,nn)lhs(pattern=>t)rhs(pattern=>t)策略或规则内的连接点具有给定执行历史的连接点具有给定arity的规则左手边控制右侧建议切入点上的操作beforeafterafterfail成功后,跑前跑后run after,i在切入点中验证代码失败run after,i在切入点中验证代码成功缠绕图四、Ausloggo连接点、连接点上下文谓词和通知变体的概要name-expr可以是一个完整的标识符名称,如EvalBinOp,也可以是一个前缀,如prop-*。name-expr的结果是一个字符串,可以使用=>x语法将其赋给变量。该模式是一个普通的migrago模式,它可能包含变量和通配符。正则表达式是执行堆栈上的一个正则表达式,可以被认为是一个扩展的,动态版本的代码内。本文将不再进一步讨论3.2切入点一个切入点是一个布尔表达式在一组固定的谓词和运算符;(和),+(或)和不,并用于指定一组连接点。在切入点中有两种谓词,连接点谓词和连接点上下文谓词3:连接点谓词是一种模式,用于挑选一组连接点。连接点上下文predicate是运行时环境上的谓词,可以在切入点中使用,以限制由连接点谓词匹配的连接点集。3这个术语和实现不同于ANOJ语言,它提供了基本切入点指示符 参见[12]。14K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5图4列出了支持的连接点和连接点上下文谓词。切入点声明是一个命名的、可选参数化的切入点,旨在允许在通知之间轻松共享相同的切入点。参数用于向通知公开切入点的细节宣言图3中的pointcut call=strategies(prop-const)示出了具有连接点谓词策略并且没有连接点上下文谓词的无参数切入点命名调用。它挑选出所有名为prop-const的策略定义。3.3建议通知是与切入点相关联的代码体有三种主要的通知,before,after和around,它们指定代码主体相对于其切入点匹配的连接点的位置图4列出了可用的建议类型为Ausloggo。 之前的代码:call=(|,““)是之前的示例。建议.策略日志由库提供,将在后面讨论。该代码将被编织到图1中的prop-const策略中,如下所示:prop-const=log(|“,const. ...........................“);(prop-const<+prop-const-assign<+prop-const-if<+prop-const-while+(all(prop-const);try(EvalBinOp)通过插入这样的通知来编写代码,为操纵当前术语提供了可能性。可以通过两种方式来控制建议主体中的策略或规则调用如何更改当前术语:建议主体是一个策略表达式,可以(完全)包装或部分地)在何处控制如何以及是否修改当前术语在上面的例子中,log只接受term参数,并且设计成不改变当前的term,使得where超级复杂。对当前术语的这种操作在around通知中非常有用,在around通知中,通知的实现者完全控制切入点应该如何执行。占位符策略程序可用于此目的。通过将继续放置在尝试中或作为部分在选项(+)中,添加故障处理策略是非常可能的。around的灵活性允许方面程序员完全覆盖和替换现有策略和规则的实现,而根本不调用proceed。这甚至可以应用于在Rightgo标准库中找到的策略。当前期限操纵的有用性源于这样一个事实,即期限通常作为当前期限从一个策略传递到另一个策略,而不是作为K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)515术语参数。例如在以下示例中,strat2将应用于strat1留下的当前项:分层1;分层2另一种更为迫切的提法是:2016 - 05 - 22 01:01:02(|r)的但这并不属于Adjugo的风格,因为当我们用其他策略组合子(如<+)替换;时,使用它会变得很麻烦。因此,当前术语操作与操作ANOJ中的输入参数和返回值非常相似用建议包装规则(或策略)包含以下方式重载规则(或策略):• 控制顺序;当使用重载扩展一组具有相同名称的规则时,我们无法控制扩展的应用顺序。如果这是必需的,规则必须被重命名,并隐藏在一个策略后面,用旧的名字来表示它们的优先级。• 控制上下文;通过使用重载扩展规则,扩展将在程序范围内始终可用。通过使用方面,我们可以控制其扩展应该可用的上下文,比如通过使用调用和代码内。3.4织造切入点被设计为在编译时完全被评估,尽管matchflow给定一个通知声明,编译器将解释它在JavaScript抽象语法树(AST)上的切入点声明,以找到在何处编织建议主体。然后,通知主体中的代码将插入到AST中的连接点之前、之后或周围。通知的主体具有基本的响应能力,这也是在编译时解决的。从图4中,我们看到建议主体可以访问规则和策略名称。Rightgo运行时没有响应或代码生成功能,因此这些名称主要用于日志记录目的。通知主体代码还可以访问来自匹配表达式的模式,并且可以在运行时评估这些模式。我们将在4.2节中展示这是如何有用的。3.5模块化与所有的JavaScript代码一样,方面必须驻留在模块中。我们认为这是明智的,因为方面是关于模块化横切关注点的。一个方面16K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5或切入点只能在方面部分中声明。虽然aspects部分可能与其他Aspergo部分(策略,规则,签名)交错,但我们鼓励每个aspect在其单独的模块中声明。首先,这有助于保持方面-独立的,交叉的关注点在设计和实现上真正独立。其次,这也允许使用编译器标记有选择地启用或禁用它们,而无需任何代码修改。ApacheGo将切入点声明保持在方面声明之外。这允许切入点在方面之间共享。在面向对象的方面呈现中,比如AASPJ,方面之间的切入点共享是使用继承捕获的:子方面从它的超方面继承所有切入点。我们在4.3节中展示了共享切入点的用处。在模块级别上控制方面应用程序所需的机制和语言特性仍有待研究。4案例研究我们描述了三个与基于规则的编程相关的案例研究中使用的AZERGOGO.第一个是一个简单的日志方面,它被包括在内,以显示与ANOJ语言的相似性和差异。第二个是一个动态类型检查器的条款实现完全作为一个方面,并显示如何方面有时可能被用作替代编译器扩展。最后一个案例是讨论方面如何在实现通用的、可适应的算法时在表达变化点方面发挥作用。4.1测井程序动作的日志记录在开发软件时通常是有用的,因此是我们想要以简洁的方式编码的问题。我们要跟踪的程序点经常遵循程序结构,例如函数的入口和出口。在这些情况下,既定的解决方案是将函数定义包装在语法或词汇宏中,这些宏可以进行简单的代码组合。这种技术的许多缺点,如降低代码可读性,缺乏可扩展性,干扰元工具(特别是用于文档和重构)和排版繁琐,都可以通过方面和它们的编织来解决。方面语言还允许在原始实现者未预料到的位置普遍插入日志代码,例如在规则条件和故障中,深入到Replego库中。图中的代码5显示了一个称为simple-logger的方面,K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)517模块简单记录器策略引用(|s)=![“Rule ",s,“' invoked”]方面pointcut log-rules(n)= rules(*=>n)aspect simple-logger =10. log-rules(r)= log-rules(|2016年10月15日<,|r)>)失败后 :log-rules(r)= log(|1999年,美国联邦储备委员会<(|r)>)aftersucceed:log-rules(r)= log(|成功,<成功(|r)>)after:log-rules(r)= log(|2015年10图五.一个完整的记录方面在阿维尼翁戈。失败、成功和完成的定义类似于调用。通过切入点声明参数的信息流的方向有些不常见:它们指定从声明中用于通过将其添加到导入列表来围绕程序中的所有规则插入日志记录代码。如果我们将simple-logger导入图2中的模块,那么EvalBinOp和EvalIf的所有执行都将被记录。织入器将通过对这两个声明进行预编译来隐藏它们,以获得一个新的、唯一的标识符。然后,它将从图中的模板生成一个包装器策略74EvalBinOp的最终编织结果如图所示六、包装器将首先执行before通知中的代码,然后执行隐藏代码。如果隐藏的代码失败,则运行after fail建议,然后是after建议。封闭的try和if-then-else分别允许after fail和aftersucceed建议将失败变为成功或将成功变为失败。建议后可能不会改变失败/成功,但可能会取代目前的任期。虽然内置的日志策略提供了在运行时设置日志级别的能力(例如,只有错误,没有警告和调试消息),但在其策略和规则定义中插入显式日志调用的程序总是会受到轻微的性能影响。Sparkgo的编码风格鼓励许多小的规则和策略,即使使用积极的内联,也对任何此类开销很敏感。因此,希望能够在最终部署之前轻松删除大多数或所有日志调用一个方面的应用可能会打开进一步的适应,再次使用方面编织。例如,图5中调用的策略可以是其他方面的目标。请注意,这些第二级或4.实际的实现使用了一个非常不易读的保护选择操作符,而不是if-then-else。if-then-else将其条件隔离在where中,使当前项保持不变。在我们的例子中,条件是切入点代码,并且必须允许修改当前项。18K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5以前;如果切入点编码,则ifafter-succeedthen try(after)elseafter; failendelseifafter-failthen try(after)elseafter;failendendEvalBinOp =(|调用(“EvalBinOp”));如果shadowed-EvalBinOp,则如果log(|,successfully(“EvalBinOp”))然后try(log(|,finished(“EvalBinOp”)else(|,finished(“EvalBinOp”)); failend其他//与then子句相同,//成功替换为失败端图六、来自Fig.的EvalBinOp声明1.在simple-logger方面织入见图7。建议编织模板。草书标识符是建议代码的插入站点。如果一个特定的通知在连接点中不存在,它将被一个id替换(after-fail被fail替换)。在我们的实施中还没有解决。在我们当前的实现中,方面是按照声明的顺序编织的。考虑以下ext-invoked的定义:方面pointcut invoked =调用(invoked)方面ext-invoked =before:调用=.如果这个方面在simple-logger之前被编织,它将没有任何效果,因为在编织ext-invoked时,任何地方都没有调用invoked。只要用户意识到这一点,并通过在simple-logger之后声明ext-invoked来手动线性化方面之间的依赖关系链,结果将符合用户的意图。K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5194.2类型检查Tencigo中的术语是用构造函数从签名中构建的,但是该语言并不对术语强制执行类型规则与签名在图1中,程序可以构造无效项,例如!Plus(Int(“0”),“0”).由于ESPRGO的正常操作模式是局部和分段重写项,可能从一个签名到另一个签名,因此无效的中间禁止不了。要调试这样的问题,通常需要手动插入调试打印,或者在日志中编织以生成程序跟踪以供手动检查,但手动验证非常容易出错。为此目的,Xplugo/XT环境附带了格式检查工具。这些工具可以应用于一个Rechgo程序的结果项,根据给定的签名检查结果项。虽然所有的签名违规都将被这些工具捕获,但它们无法帮助您判断程序中的实际问题所在,因为检查完全发生在程序执行之后。使用方面,我们可以将格式检查器编织到程序规则中,精确地编织到我们希望由签名指定的结构不变量所保持的位置。图8中的类型检查器方面使用了Xplego/XT中的格式检查器功能,将格式检查编织到一个JavaScript程序中的所有规则中。 通过修改类型检查规则切入点,用户可以控制类型检查器的精确应用。 它的用法类似于simple-logger:它必须被导入,并且必须在strategies部分声明相关签名的类型检查策略:(|t)= format-check-Imp(|t)的范围内图中的签名1是一个名为Imp的小型命令式语言的签名摘录。给定一种语言语法,Xpluggo/XT格式检查工具将生成一个包含该语法的完整格式检查器的Xpluggo模块这个格式检查器的顶级策略名为format-check-。它可以应用于一个术语,看看它是否是该语言的有效(子)术语。与日志记录一样,引入这个方面为用户提供了一个快速而简洁的机制来决定程序的哪些部分(如果有的话)应该进行类型检查,并且它的使用可以在编译和运行时进行切换(后者总是会引起小的性能影响,如前所述)。类型检查的参数t是类型检查-规则切入点匹配的模式,因此是规则的右侧模式如果t是一个项(没有变量),理论上可以在编译时完全检查它,因为签名和项对于编译器。在t包含变量的情况下,静态部分可以在编译时检查,但变量部分必须在运行时计算如果我们20K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5模块类型检查-示例方面pointcut typecheck-rules(n,t)= rules(n);rhs(t)aspect typechecker =around(n,t):typecheck-rules(n,t)= proceed ;(|t)的范围内<+(log(|incorrect-term(n); fail))见图8。一个方面,用于将简单的动态类型的术语编织到规则中。假设只有规则可以构造项,并且只有在它们的右侧模式中,我们可以通过仅检查变量的顶部项是其封闭的“静态”项的有效子项来进一步优化匹配目前,方面编译器并不执行这样的优化,而是依赖于MatchGo编译器后端的匹配优化器类型检查方面只是内置类型系统的部分替代,特别是因为它不进行类型推理,因此无法消除冗余检查。[16]中讨论了类型化的、策略性的术语重写的主题4.3扩展算法图1中的算法是前向传播的更一般的数据流问题的一个实例,其示例是公共子表达式消除、复制传播、不可达代码消除和绑定变量重命名。该算法可以分解为特定于语言的骨架和特定于问题的扩展。骨架需要为每种语言实现一次,因为它只依赖于语言结构和作用域规则。变量点是程序中可以插入实体的变量的具体点。通过提供明确定义的变化点,骨架可以适应手头的特定传播问题。4.3.1表达适应性算法有许多众所周知的技术来表达自适应算法。当提供一个算法,这是应该被重用,并适应其他程序员(用户),我们是后的技术,这可能:• 适应性;我们希望最大的自由,我们可以暴露给我们的用户的变化点。• 重用;我们的算法的用户应该需要尽可能少的代码重新实现。这一点在维护工作中尤为重要K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)521• 可追溯性;当我们的算法中发现错误(无论是设计还是实现)时,我们希望为用户提供一个简单的升级路径。理想情况下,他们只需要替换我们算法所在的库文件这可能并不总是可行的,但至少,我们希望用户知道他的系统的哪些部分可能会受到错误的影响。• 进化;我们必须能够在不打扰用户的情况下改变算法的内部结构。Boilerplates最流行但最不受欢迎的适应技术之一是样板适应。在这种方法中,代码模板被手动复制,然后修改以适应手头的情况。该方法支持由于固有的代码重复而导致的高维护成本。如果后来发现原始模板包含严重的(安全)错误,则问题尤其严重,因为无法跟踪它的使用位置。另一方面,由于可以达到所有的变化点,因此它具有非常高的可伸缩性。在最极端的情况下,样板适应允许申请人逐渐取代整个算法。设计模式另一种流行的重用技术是设计模式[6]。设计模式是一种可重用的工程知识。 对于每一个设计模式适用的情况,它必须由程序员从头开始实现。近年来,许多研究都致力于提高设计模式的重用,无论是通过提供直接的语言支持[4,9]还是将它们放在可重用的库中[1,8]。钩子和回调钩子和回调是众所周知的技术,用于通过库或算法的用户可以扩展的可重写存根来暴露变化通过调用注册函数,用户可以添加回调和钩子,这些回调和钩子在算法中的预定位置处被调用,或者在程序中的特定事件时被调用。只要保持算法和它的回调之间的契约,算法内部就可以从适配的钩子中独立地进化,因此具有良好的可维护性。它的缺点包括这样的事实,即不是所有的变化点都可以表示为钩子,并且很难在同一程序内的多个上下文中适应具有不同钩子集的算法。这在某种程度上可以通过使用限定作用域的动态规则来解决。对于其他范例,函数指针、闭包和/或对象允许存在多个上下文22K.T. Kalleberg,E.Visser/Electronic Notes in Theoretical Computer Science 147(2006)5高阶参数在函数式语言中,通常通过高阶参数来暴露变化点。论文[20]描述了一种使用这种方法进行前向传播的自适应骨架。该技术提供了一种精确的方法来暴露变化点,这既易于使用,又允许用户在同一程序中根据上下文调整算法。一个缺点是“参数过多”的问题:我们希望用户处理的参数的数量。在问题空间很大的情况下,算法通常会有许多变化点,产生一个很长的参数列表。这个问题的一个常见解决方案是在算法中提供多个入口点,每个入口点都有越来越多的参数,或者在语言支持的情况下,让参数具有默认值。4.3.2限制样板和设计模式
下载后可阅读完整内容,剩余1页未读,立即下载
cpongm
- 粉丝: 5
- 资源: 2万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 基于Python和Opencv的车牌识别系统实现
- 我的代码小部件库:统计、MySQL操作与树结构功能
- React初学者入门指南:快速构建并部署你的第一个应用
- Oddish:夜潜CSGO皮肤,智能爬虫技术解析
- 利用REST HaProxy实现haproxy.cfg配置的HTTP接口化
- LeetCode用例构造实践:CMake和GoogleTest的应用
- 快速搭建vulhub靶场:简化docker-compose与vulhub-master下载
- 天秤座术语表:glossariolibras项目安装与使用指南
- 从Vercel到Firebase的全栈Amazon克隆项目指南
- ANU PK大楼Studio 1的3D声效和Ambisonic技术体验
- C#实现的鼠标事件功能演示
- 掌握DP-10:LeetCode超级掉蛋与爆破气球
- C与SDL开发的游戏如何编译至WebAssembly平台
- CastorDOC开源应用程序:文档管理功能与Alfresco集成
- LeetCode用例构造与计算机科学基础:数据结构与设计模式
- 通过travis-nightly-builder实现自动化API与Rake任务构建
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功