Interface F#:未找到与此重写对应的抽象属性
各位飞越者好。我正在进行一个小组项目,创建一个光线跟踪器,绘制3D场景的2D渲染。我目前的任务是对需要移动、镜像、剪切等的对象(形状)进行矩阵变换 在使用形状时,我们选择实现一个定义命中函数类型的接口。此命中函数在每个形状中定义,例如球体、长方体、平面等。当变换形状时,我需要变换击中形状的光线,并且这样做的方式似乎是使用一个更高阶的函数来改变原始的命中函数 为了做到这一点,我已经实现了transformHitFunction函数,它似乎可以工作,但是实现Shape接口的新型transformedShape给了我一个错误 未找到与此重写对应的抽象属性 这对我来说没有任何意义,因为它与相同类型的其他命中函数一起工作。有人能看出哪里不对劲吗 我试图去掉所有与此问题无关的模块、名称空间和代码Interface F#:未找到与此重写对应的抽象属性,interface,f#,higher-order-functions,Interface,F#,Higher Order Functions,各位飞越者好。我正在进行一个小组项目,创建一个光线跟踪器,绘制3D场景的2D渲染。我目前的任务是对需要移动、镜像、剪切等的对象(形状)进行矩阵变换 在使用形状时,我们选择实现一个定义命中函数类型的接口。此命中函数在每个形状中定义,例如球体、长方体、平面等。当变换形状时,我需要变换击中形状的光线,并且这样做的方式似乎是使用一个更高阶的函数来改变原始的命中函数 为了做到这一点,我已经实现了transformHitFunction函数,它似乎可以工作,但是实现Shape接口的新型transformed
type Transformation = Matrix of float [,]
type Vector =
| V of float * float * float
let mkVector x y z = V(x, y, z)
let vgetX (V(x,_,_)) = x
let vgetY (V(_,y,_)) = y
let vgetZ (V(_,_,z)) = z
type Point =
| P of float * float * float
let mkPoint x y z = P(x, y, z)
let pgetX (P(x,_,_)) = x
let pgetY (P(_,y,_)) = y
let pgetZ (P(_,_,z)) = z
type Material = Material
type Texture =
| T of (float -> float -> Material)
type Shape =
abstract member hit: Point * Vector -> (Texture*float*Vector) option
let transformPoint (p:Point) t =
match t with
| Matrix m -> mkPoint ((pgetX(p))*m.[0,0] + (pgetY(p))*m.[0,1] + (pgetZ(p))*m.[0,2] + m.[0,3])
((pgetX(p))*m.[1,0] + (pgetY(p))*m.[1,1] + (pgetZ(p))*m.[1,2] + m.[1,3])
((pgetX(p))*m.[2,0] + (pgetY(p))*m.[2,1] + (pgetZ(p))*m.[2,2] + m.[2,3])
let transformVector (v:Vector) t =
match t with
| Matrix m -> mkVector ((vgetX(v))*m.[0,0] + (vgetY(v))*m.[0,1] + (vgetZ(v))*m.[0,2] + m.[0,3])
((vgetX(v))*m.[1,0] + (vgetY(v))*m.[1,1] + (vgetZ(v))*m.[1,2] + m.[1,3])
((vgetX(v))*m.[2,0] + (vgetY(v))*m.[2,1] + (vgetZ(v))*m.[2,2] + m.[2,3])
let transformHitFunction fn (t:Transformation) =
fun (p:Point,v:Vector) ->
let tp = transformPoint p t
let tv = transformVector v t
match fn(tp,tv) with
| None -> None
| Some (tex:Texture, d:float, n) -> let tn = transformVector n t
Some (tex, d, tn)
type transformedShape (sh:Shape, t:Transformation) =
interface Shape with
member this.hit = transformHitFunction sh.hit t
简短回答 在实现或重写成员时遇到问题,请提供与抽象或虚拟成员定义完全相同的参数列表。(另外,请注意括号,因为附加括号可以微妙地更改成员的类型。) 例如,在这种情况下:
成员this.hit(arg1,arg2)=…
回答稍微长一点
您遇到的情况是,F#的一流函数与其对面向对象方法的支持之间的差异是相关的
为了与公共语言基础设施(CLI)的面向对象语言(以及F#程序中的面向对象编程风格)兼容,F#有时不仅区分函数和值,甚至区分面向对象和函数风格中的函数
F#在两件事上使用了非常相似的语法:“经典”CLI方法采用参数列表(还支持重载和可选参数),而F#自己最喜欢的函数类型FSharpFunc
,它总是采用一个参数,但支持curry,并且可以通过元组采用多个参数。但这两者的语义可能不同
问题的最后一行尝试传递一个带有元组输入的函数,以实现一个采用两个参数的方法,就像C#或VB.NET中的方法采用它们一样:CLI方法的参数列表。直接指定一个F#风格的一级函数在这里不起作用,否则将使用一个元组参数;编译器坚持显式获取每个参数。如果使用完整的方法参数列表编写实现,它将正常工作。例如:
member this.hit (arg1, arg2) = transformHitFunction sh.hit t (arg1, arg2)
另一个解决方案是将hit
声明为:
abstract member hit: (Point * Vector -> (Texture*float*Vector) option)
(注意括号!)现在它是一个包含一级函数的属性;您可以通过返回这样的函数来实现它,但是成员的类型发生了微妙的变化
后者就是为什么要将原始接口实现为单参数函数,例如:
member this.hit a = transformHitFunction sh.hit t a // error
这是行不通的。更准确地说,编译器将拒绝将a
视为元组。同样的问题也适用于
member this.hit ((arg1, arg2)) = transformHitFunction sh.hit t (arg1, arg2) // error
现在怎么了?外圆括号定义参数列表,但内圆括号使用元组模式分解单个参数!因此参数列表仍然只有一个参数,编译失败。编写方法时,最外层的括号和逗号与其他地方使用的元组不同,即使编译器在某些情况下在这两者之间进行转换。目前,您的
transformedShape.hit
是一个非索引属性。调用时,它返回一个函数,您需要提供一个Point*Vector
元组,您将得到想要的结果。如果添加助手绑定,您将能够更好地看到这一点:将鼠标悬停在f
上:
type transformedShape (sh:Shape, t:Transformation) =
interface Shape with
member this.hit =
let f = transformHitFunction sh.hit t
f
正如其他人已经说过的,你所需要做的就是清楚地阐明论点,你很好:
type transformedShape2 (sh:Shape, t:Transformation) =
interface Shape with
member this.hit(p, v) = transformHitFunction sh.hit t (p, v)
this.hit
并不是一个真正的函数——它需要一些论证。必须承认,你写这一切的唯一目的就是能够编写sh.hit
;)该死,我被抓住了!我甚至试着用额外的h来隐藏它…谢谢你快速而深入的回复!