如何编写一个通用的Scala函数特性? 问题 第一种方法
如果你想如何编写一个通用的Scala函数特性? 问题 第一种方法,scala,Scala,如果你想 trait Distance extends ((SpacePoint, SpacePoint) => Double) object EuclideanDistance extends Distance { override def apply(sp1: SpacePoint, sp2: SpacePoint): Double = ??? } trait Kernel extends (((Distance)(SpacePoint, SpacePoint)) =>
trait Distance extends ((SpacePoint, SpacePoint) => Double)
object EuclideanDistance extends Distance {
override def apply(sp1: SpacePoint, sp2: SpacePoint): Double = ???
}
trait Kernel extends (((Distance)(SpacePoint, SpacePoint)) => Double)
object GaussianKernel extends Kernel {
override def apply(distance: Distance)(sp1: SpacePoint, sp2: SpacePoint): Double = ???
}
然而,对象GaussianKernel Extendes内核的应用
并不是特征内核的应用
的一个例外
第二种方法-编辑:结果表明,这毕竟是可行的。。。
或者我可以写
trait Kernel extends ((Distance) => ( (SpacePoint, SpacePoint) => Double))
object GaussianKernel extends Kernel {
override def apply(distance: Distance): (SpacePoint, SpacePoint) => Double =
(sp1: SpacePoint, sp2: SpacePoint) =>
math.exp(-math.pow(distance(sp1, sp2), 2) / (2))
}
但我不确定这是不是在讨好
编辑:结果表明,我可以以一种讨好的方式使用第二种方法。我认为这正是典型的咖喱,只是没有语法上的糖分
对这一想法的解释
这个想法是:对于我的算法,我需要一个。这个内核为空间中的两个向量计算一个度量——这里是SpacePoint
s。为此,内核需要一种方法来计算两个SpacePoint
s之间的距离。距离和内核都应该是可交换的(),因此我将它们声明为trait(在Java中,我将它们声明为接口)。这里我使用(未显示)和。为什么要咖喱?稍后当使用这些东西时,所有测量的距离将或多或少相同,而空间点将一直变化。再次强调,要坚持开闭原则。因此,在第一步中,我希望用距离预先配置GaussianKernel
(如果您愿意),并返回函数
,该函数可以稍后在程序中使用空间点
s馈送(我确信代码是错误的,只是为了让您了解我的目标):
问题
我如何建立我的特质
既然不同的高斯角
的距离可以不同,那么高斯角
应该是一个类而不是一个对象吗
我是否应该部分地应用GaussianKernel
而不是curry
我的方法是否不好,GaussianKernel
应该是一个在字段中存储距离的类
我只会使用函数。所有这些额外的东西都是复杂的,使事物具有特质似乎并没有增加任何东西
def euclideanDistance(p1: SpacePoint1, p1: SpacePoint1): Double = ???
class MyClass(kernel: (SpacePoint, SpacePoint) => Double) { ??? }
val myClass = new MyClass(euclideanDistance)
因此,只需将内核作为一个函数传递,该函数将计算给定两点的距离
我在打电话,所以不能完全检查,但这会给你一个想法
这将允许您在需要时部分应用这些功能。假设你有一个基本的计算方法
def calc(settings: Settings)(p1: SpacePoint1, p1: SpacePoint1): Double = ???
val standardCalc = calc(defaultSettings)
val customCalc = calc(customSettings)
我会首先将所有东西建模为函数,然后仅在需要时将共性汇总到特征中。我只会使用函数。所有这些额外的东西都是复杂的,使事物具有特质似乎并没有增加任何东西
def euclideanDistance(p1: SpacePoint1, p1: SpacePoint1): Double = ???
class MyClass(kernel: (SpacePoint, SpacePoint) => Double) { ??? }
val myClass = new MyClass(euclideanDistance)
因此,只需将内核作为一个函数传递,该函数将计算给定两点的距离
我在打电话,所以不能完全检查,但这会给你一个想法
这将允许您在需要时部分应用这些功能。假设你有一个基本的计算方法
def calc(settings: Settings)(p1: SpacePoint1, p1: SpacePoint1): Double = ???
val standardCalc = calc(defaultSettings)
val customCalc = calc(customSettings)
首先,我会将所有事物建模为函数,然后仅在需要时将共性汇总到特征中。答案
1.我如何建立我的特质?
第二种方法是要走的路。你不能像往常一样使用咖喱的语法,但这与咖喱是一样的:
GaussianKernel(ContinuousEuclideanDistance)(2, sp1, sp2)
GaussianKernel(ContinuousManhattanDistance)(2, sp1, sp2)
val eKern = GaussianKernel(ContinuousEuclideanDistance)
eKern(2, sp1, sp2)
eKern(2, sp1, sp3)
val mKern = GaussianKernel(ContinuousManhattanDistance)
mKern(2, sp1, sp2)
mKern(2, sp1, sp3)
object GaussianKernel extends Kernel {
override def apply(distance: Distance)(sp1: SpacePoint, sp2: SpacePoint): Double = ???
}
为什么第一种方法不起作用
因为咖喱只能用于方法(duh…)。问题始于这样一个概念,即函数非常像一个方法,只是实际的方法是apply方法,它是通过调用函数的“构造函数”来调用的
首先:如果一个对象有一个apply方法,那么它已经有了这个能力——不需要扩展函数。扩展函数只会强制对象使用apply方法。当我在这里说“object”时,我指的是一个单例Scala对象(标识符为object
)和一个实例化的类。如果对象是一个实例化类MyClass
,那么调用MyClass(…)
引用构造函数(因此需要一个new
),并且应用被屏蔽。但是,在实例化之后,我可以按照前面提到的方式使用生成的对象:val myClass=new myClass(…)
,其中myClass是一个对象(类实例)。现在我可以编写myClass(…)
,调用apply方法。如果对象是单例对象,那么我已经有了一个对象,可以直接编写MyObject(…)
来调用apply方法。当然,一个对象(在两种意义上)没有构造函数,因此apply没有被屏蔽,可以使用。完成后,它看起来就像一个构造函数,但事实并非如此(这就是Scala语法——只是因为它看起来相似,并不意味着它是同一件事)
第二:咖喱是一种语法上的糖:
def mymethod(a: Int)(b: Double): String = ???
语法上的糖是什么
def mymethod(a: Int): ((Double) => String) = ???
def mymethod(a: Int): Function1[Double, String] = ???
哪个是语法糖
def mymethod(a: Int): ((Double) => String) = ???
def mymethod(a: Int): Function1[Double, String] = ???
因此
(如果我们扩展一个函数N[T1,T2,…,Tn+1],它是这样工作的:最后一个类型Tn+1是apply方法的输出类型,前N个类型是输入类型。)
现在,我们希望这里的apply方法应该是:
GaussianKernel(ContinuousEuclideanDistance)(2, sp1, sp2)
GaussianKernel(ContinuousManhattanDistance)(2, sp1, sp2)
val eKern = GaussianKernel(ContinuousEuclideanDistance)
eKern(2, sp1, sp2)
eKern(2, sp1, sp3)
val mKern = GaussianKernel(ContinuousManhattanDistance)
mKern(2, sp1, sp2)
mKern(2, sp1, sp3)
object GaussianKernel extends Kernel {
override def apply(distance: Distance)(sp1: SpacePoint, sp2: SpacePoint): Double = ???
}
也就是说
object GaussianKernel extends Kernel {
def apply(distance: Distance): Function2[SpacePoint, SpacePoint, Double] = {
new Function2[SpacePoint, SpacePoint, Double] {
def apply(SpacePoint, SpacePoint): Double
}
}
}
那么,GaussianKernel
应该扩展什么(或者什么是GaussianKernel
)?它应该扩大
Function1[Distance, Function2[SpacePoint, SpacePoint, Double]]
(这与距离=>((SpacePoint,SpacePoint)=>Double)相同)
,第二种方法)
现在的问题是,这不能写为curry,因为它是一个类型描述,而不是一个方法的签名。在讨论了所有这些之后,这似乎是显而易见的,但在讨论所有这些之前,它可能没有。问题是,类型描述似乎直接转换为apply方法的签名(第一个或唯一一个,取决于如何分解语法糖),但事实并非如此。不过,公平地说,它本可以在编译器中实现:类型描述和apply方法的