F#:找不到与此覆盖相对应的抽象属性

时间:2016-04-21 12:48:55

标签: interface f# higher-order-functions

你好伙伴Overflowers。我正在开发一个组项目,用于创建绘制3D场景的2D渲染的光线跟踪器。我目前正在进行的任务涉及对象(形状)的矩阵变换,需要移动,镜像,剪切等。

在处理形状时,我们选择实现一个定义命中函数类型的接口。此命中函数在每个形状中定义,例如球体,方框,平面等。当变换形状时,我需要变换撞击形状的光线,并且这样做的方式似乎是具有更高阶函数,改变原始形状点击功能。

为了做到这一点,我已经实现了函数transformHitFunction,这似乎有效,但实现Shape接口的新类型transformedShape给了我错误

  

未找到与此覆盖

对应的抽象属性

这对我没有任何意义,因为它适用于同类型的其他命中功能。谁能发现什么是错的?

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

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

2 个答案:

答案 0 :(得分:8)

简短回答

如果在实现或覆盖成员时遇到问题,请在抽象或虚拟成员的定义中提供参数列表完全。 (另外,请注意您的括号,因为额外的括号可以以微妙的方式更改成员的类型。)

E.g。在这种情况下:member this.hit (arg1, arg2) = ...

答案稍长

您遇到的情况是F#的第一类函数与其对面向对象样式方法的支持之间存在差异。

为了兼容公共语言基础设施(CLI)的面向对象语言(以及F#程序中面向对象的编程风格),F#有时不仅区分函数和值,而且甚至区分面向对象和函数式函数

F#使用非常相似的语法来做两件事:采用参数列表(并且还支持重载和可选参数)的“经典”CLI方法与F#自己喜欢的函数类型FSharpFunc,它总是需要一个参数但是支持currying,可以通过元组获取多个参数。但这两者的语义可能不同。

问题的最后一行尝试传递一个带有tupled输入的函数来实现一个方法,该方法采用两个参数,就像C#或VB.NET中的方法一样:CLI方法的参数列表。直接分配一个F#风格的第一类函数在这里不起作用,并且nether将是一个单元组参数;编译器坚持要明确地获取每个参数。如果使用完整的方法参数列表编写实现,它将起作用。例如:

    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

现在怎么了?外括号定义参数列表,但内括号使用元组模式来分解单个参数!因此参数列表仍然只有一个参数,编译失败。编写方法时最外面的括号和逗号与其他地方使用的元组不同,即使编译器在某些情况下在两者之间进行转换。

答案 1 :(得分: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)