2016-04-21 40 views
2

你好同胞Overflowers。我正在开发一个小组项目来创建一个绘制3D场景的2D渲染的光线跟踪器。我目前正在进行的任务涉及对象(形状)的矩阵变换,需要移动,镜像,剪切等。F#:没有发现与此覆盖对应的抽象属性

在处理形状时,我们选择实现一个接口来定义一个命中函数的类型。这种命中功能是在每个形状中定义的,例如球体,盒子,平面等。当转换一个形状时,我需要转换命中形状的射线,以及做这些的方式似乎是用更高阶的函数来改变原始形状打功能。

为了做到这一点,我已经实现的功能transformHitFunction,这似乎工作,但新类型transformedShape,实现了Shape接口,是给我的错误

没有抽象的财产被发现对应于此覆盖

这对我来说没有任何意义,因为它与其他同类型的命中功能一起工作。任何人都可以发现什么是错的?

我试图去除与此问题无关的所有模块,名称空间和代码。

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 
+1

'this.hit'是不是一个真正的功能 - 它需要采取一些参数 –

+5

只是承认,你写了这一切与能够编写'sh.hit'单一目的; ) –

+0

该死的,我被抓住了!我甚至试图隐藏它额外的... –

回答

6

简短的回答

具有实施或压倒一切的成员的问题时,提供的参数列表正是作为抽象的或虚拟成员的定义。 (另外,请注意括号,因为额外的括号可以以微妙的方式改变成员的类型。)

例如,在这种情况下:member this.hit (arg1, arg2) = ...

稍长回答

您遇到的情况是,F#的一流功能,它支持面向对象式的方法之间的差异是相关的。为了与公共语言基础结构(CLI)的面向对象语言(以及F#程序中的面向对象编程风格)兼容,F#有时不仅区分函数和值,甚至还区分面向对象和功能中的函数样式。 F#使用非常相似的语法来处理两件事情:采用参数列表(也支持重载和可选参数)的“古典”CLI方法与F#自己喜欢的函数类型FSharpFunc,它总是只有一个参数,但支持柯里和可能通过元组采用多个参数。但是这两者的语义可能不同。

该问题的最后一行尝试传递一个带有元组输入的函数,以实现一种方法,该方法以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 

现在有什么问题?外部圆括号定义参数列表,但内部圆括号使用元组模式分解单个参数!所以参数列表仍然只有一个参数,编译失败。写入方法时最外面的括号和逗号与其他地方使用的元组是不同的特性,即使编译器在某些情况下在两者之间进行了翻译。

+0

谢谢你的迅速和深入的答复! –

4

此刻,您的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)