2016-05-05 71 views
0

如果我有一个任意大小的正方形网格(在WPF/XAML中创建),并且每边有给定数量的单元格,它应该是可笑的容易计算坐标到通过获取相对于网格元素的鼠标光标位置点击的单元格。当我尝试学习F#时,我遇到了一些语法问题,并希望得到一些输入。F#,从相对于IInputElement的鼠标光标位置提取网格坐标

这里是什么,我试图做一个例子:

// From a mouse event, fetch the position of the cursor relative to a IInputElement 
    let get_mouse_position (relative_to : IInputElement) (args : Input.MouseEventArgs) : (int*int) = 
     let position = args.GetPosition relative_to 
     ((Convert.ToInt32(position.X)/cellsPerSide), (Convert.ToInt32(position.Y)/cellsPerSide)) 

    // Get the position of the cursor relative to the grid 
    let get_cell_coordinates (element : IInputElement) = 
     get_mouse_position element 

然而,当我尝试使用通过调用get_cell_coordinates其他地方,其中需要(X,Y)坐标,我得到检索到的坐标一个错误,指出:

这种表达预计将有一个int类型* INT但这里的类型是“A - > INT * INT

那么,什么是我做错了,为什么我得到这个多态类型而不仅仅是一个整数的元组?

+0

您忘了在'get_mouse_position元素'中添加参数。习惯这个错误信息;它会发生很多:) – Ray

回答

0

我解决了它通过使用静态Mouse.GetPosition方法来获取鼠标的位置,而不是Input.MouseEventArgs。

现在,该代码如下所示,如果任何人有同样的问题:

// From a mouse event, fetch the position of the cursor relative to a IInputElement 
let get_mouse_position (relative_to : IInputElement) : (int*int) = 
    let position = Mouse.GetPosition relative_to 
    ((Convert.ToInt32(position.X)/cellsPerSide), (Convert.ToInt32(position.Y)/32)) 

// Get the position of the cursor relative to the input element 
let get_cell_coordinates (element : IInputElement) = 
    get_mouse_position element 
2

你得到的类型是不是一个“多晶型”类型,它是一个函数类型。您得到类型'a -> int * int而不是您期望的int * int结果的原因是您没有将所有参数传递给您的函数,因此F#返回了一个函数,该函数需要其余参数。这就是所谓的“部分应用程序”,你可以在这里阅读更多关于它:

https://fsharpforfunandprofit.com/posts/currying/

https://fsharpforfunandprofit.com/posts/partial-application/

两篇文章的简要总结:在F#中,所有功能都视为取一个参数并返回一个结果。是的,所有功能。当您创建一个看起来需要两个参数的函数时,F#会在内部重写它。它变成一个函数,它接受一个参数并返回第二个函数;这第二个函数接受一个参数并返回结果。在这一点上,一个具体的例子可能会有用。考虑一下这个功能:

let doubleAndSubtract a b = (a * 2) - b 

(显然,围绕a * 2括号并不真正需要,但我离开了他们,使功能明确的阅读)。

内部,F#实际上重写此功能为以下:

let doubleAndSubtract a = 
    let subtract b = (a * 2) - b 
    subtract 

换句话说,它构建一个功能“关闭了”(捕获),您传入的a值。所以,下面的两个功能是完全等价的:

let explicitSubtract b = (5 * 2) - b 
let implicitSubtract = doubleAndSubtract 5 

如果您在到F#交互提示符下键入这些功能,并期待在其声明每个函数有类型,explicitSubtract的类型为b:int -> int,并implicitSubtract的类型是int -> int 。这两者之间的唯一区别是explicitSubtract的类型命名参数,而implicitSubtract不会“知道”其参数的名称。但是两者都会接受一个int,并返回一个int(具体来说,是减去参数)。

现在,让我们来看看doubleAndSubtract的类型签名。如果您将其输入到F#交互式提示符中,则会看到它的类型签名为int -> int -> int。在类型签名中,->是右关联的,所以签名实际上是int -> (int -> int) - 换句话说,函数接受一个int并返回一个接受int并返回int的函数。 但是你也可以把它看作一个函数,它需要两个int并返回一个int,并且你可以这样定义它。

那么,发生了什么与您的代码这里是你定义这样的功能:

let get_mouse_position (relative_to : IInputElement) (args : Input.MouseEventArgs) : (int*int) = ... 

这是一个有两个参数(类型IInputElementInput.MouseEventArgs),并返回整数的元组的功能。 ... 这相当于一个函数,该函数接受IInputElement类型的一个参数,并返回一个函数,该函数需要一个Input.MouseEventArgs并返回一个包含两个整数的元组。换句话说,你的函数签名是:

IInputElement -> Input.MouseEventArgs -> (int * int) 

当你叫它为get_mouse_position element你通过它只有一个参数,你有这么什么Input.MouseEventArgs -> (int * int)类型的函数。类型检查器将此类型报告为类型'a -> int * int(将Input.MouseEventArgs更改为泛型类型名称'a),原因我不想进入此处(这已经很长),但这就是为什么您得到了结果。

我希望这可以帮助您更好地理解F#函数。并阅读我链接的两篇文章;他们会进一步理解你的理解。

+0

非常感谢您的详细解答,现在更清晰! –