Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Generics 代数表达式-Kotlin为静态和动态属性建模的方法_Generics_Kotlin_Dynamic_Static_Expression - Fatal编程技术网

Generics 代数表达式-Kotlin为静态和动态属性建模的方法

Generics 代数表达式-Kotlin为静态和动态属性建模的方法,generics,kotlin,dynamic,static,expression,Generics,Kotlin,Dynamic,Static,Expression,为冗长的介绍道歉,不幸的是,我想不出一个办法来缩短它 问题 我们正在构建一个简单的应用程序,通过应用模式转换来操纵代数表达式-目标是能够说“获取2*x+2并应用“分解”。最终的目标是能够确定在一组模式转换下两个表达式是否相等(即是2*(x+1)^2“可从2x^2+4x+2”到达) 我们已经创建了一组对域进行建模的类,并定义了一个“规范”形式,这基本上意味着有一种方法(象征性地)构造相同的表达式:x+y+z将始终表示为Plus(x,y,z),而不是Plus(y,x,z)或Plus(x,Plus(y

为冗长的介绍道歉,不幸的是,我想不出一个办法来缩短它

问题 我们正在构建一个简单的应用程序,通过应用模式转换来操纵代数表达式-目标是能够说“获取
2*x+2
并应用“分解”。最终的目标是能够确定在一组模式转换下两个表达式是否相等(即是
2*(x+1)^2
“可从
2x^2+4x+2
”到达)

我们已经创建了一组对域进行建模的类,并定义了一个“规范”形式,这基本上意味着有一种方法(象征性地)构造相同的表达式:
x+y+z
将始终表示为
Plus(x,y,z
),而不是
Plus(y,x,z)
Plus(x,Plus(y,z))
,即使它是通过调用
Plus(Plus(x,z),y)
创建的

其中许多规范变换是相应运算符代数性质的直接结果-结合性允许上述“展平”等。这些性质也对模式运算有直接影响-例如,
Power(\\,2)
是不可交换的,因此,它只匹配具有
2
作为第二个子项(参数)的表达式,但是
Plus(u,2)
是可交换的,因此它可以匹配具有
2
作为其任何子项的表达式。出于这些原因,我们希望将这些代数属性表示为类上的显式数据,然后在应用程序的各个部分中解析这些数据

棘手的部分是,我们需要静态地(我们在构造表达式以应用适当的规范转换时需要它,但由于表达式尚未构造,我们无法动态地访问它)和动态地(在模式匹配期间,我们必须能够执行
expr.hasAttribute)使用此数据(关联)
)。由于这些属性对于特定的表达式类型(类)是固定的,因此我们还试图避免直接在类上定义它,例如

class Plus(children: List<Expression>): Expression(children) {
    val attributes = setOf(Associative, Commutative, IdentityElement(Number(0.0)))
    ...
}

您可以拥有set属性,而无需每次创建新实例,例如:

private val plusAttributes = setOf(Associative, Commutative, IdentityElement(Number(0.0)))

class Plus(children: List<Expression>): Expression(children) {
    val attributes = plusAttributes
    // ...
}
private val plusAttributes=setOf(关联、交换、标识元素(数字(0.0)))
class Plus(子级:列表):表达式(子级){
val属性=加值属性
// ...
}
这样所有Plus实例都有一个对同一个集合的引用。每个实例中仍然有额外引用的开销,但远远小于集合的开销(集合可以有一个大的对象树)

更好的是,将
属性
作为
表达式
类中的抽象属性,在每个实现类中重写。这样,您就可以在不知道表达式类型的情况下访问属性


另一种方法,你可以考虑使用标记子接口,除了扩展表达式之外,相关子类也可以实现。这根本没有开销,而且非常容易和清晰地测试:<代码>(MyExt表达式是关联的)< /代码>,虽然它不给你一张地图。(如果有任何与属性相关的行为,您可以向相关接口添加一个方法,这将朝向更传统的OO设计。)

感谢您提供的提示,但是在这两个方面,我都不清楚如何实现静态访问,即在Builder中-在这一点上没有实例,因此我无法访问它们。我可以想到两种解决方案:创建一个“虚拟”表达式以允许动态访问,这似乎是错误的,而且也无法保证“每个表达式都是标准形式”或者2)在类和伴随对象上都有“属性”,这就是“可怕的代码复制”"我在前面引用过,忽略了同伴对象不这样做的事实inherit@GabrielShanahan啊,我现在明白你所说的“静态和动态”是什么意思了……如果在我的示例代码中,你将
plusAttributes
移动到伴生对象中,并将其重命名为
attributes,可能不会涉及太多重复
?然后您可以“静态”访问它。(请注意,在Kotlin中,伴随对象可以扩展另一个类和/或实现接口。因此您需要以多态方式访问它,它可以实现一些公共接口。)但我恐怕我错过了太多的大局,无法提供太多帮助。谢谢你的回答。我理解你的意思,但即使我让伴生对象扩展一个公共接口,我仍然无法向编译器保证在派生表达式的类上存在伴生对象。每个子类(加上,时间,符号,战俘)必须定义它自己的同伴,但即使它扩展了一个公共接口,编译器也不允许我在没有明确指定类型的情况下访问它:ie.Plus.attributes有效,但t::attributes无效,这阻止了我对代码进行抽象。唯一的解决方案是反射。@GabrielShanahan我认为有一种方法这不需要反射。如果所有同伴都使用名为
attributes
的属性实现了一个
hasaAttributes
接口,那么您只需使用一个cast:
(t::class.companyObjectInstance as hasaAttributes)。attributes
。(这仍然很难看,很遗憾你不能通过在超类中指定一个限制来避免施法,当然,但也许这总比没有好?)另外,谢谢你的讨论,这有助于我了解我和其他人一样在思考这个问题。看看我发布的代码,你是否看到我在这方面做的任何可能的改进?例如,我真的很想解决一个丑陋的问题,在canonizeByAttributes中到处都有回报,但是我唯一能想到的就是转换回报率
private val plusAttributes = setOf(Associative, Commutative, IdentityElement(Number(0.0)))

class Plus(children: List<Expression>): Expression(children) {
    val attributes = plusAttributes
    // ...
}