我写了一个函数来找出多边形的面积和质心(基于这个参考:http://en.wikipedia.org/wiki/Centroid)
但是,我对F#很新,我不知道如何将这个函数从VB.NET转换为F#(VS2010)。如果有人可以帮助我,那将是非常感激的(假设在F#中我已经有一个名为Point2D的类型,并且F#函数的输入是point2D的列表)。我的第一次尝试是在VB代码之下。但是我不喜欢我的版本,因为它必须逐步计算Cx,Cy和A - 这不是我的VB代码对应的真实反映
Private Function Find_Centroid_And_Area(ByVal List As List(Of Point2D)) As AreaCentroid
Dim result As New AreaCentroid()
Try
Dim Qx As Double = 0
Dim Qy As Double = 0
Dim A As Double = 0
Dim Cx As Single = 0
Dim Cy As Single = 0
Dim P1 As Point2D = Nothing
Dim P2 As Point2D = Nothing
For i As Integer = 0 To List.Count - 1
P1 = List(i)
Select Case i
Case List.Count - 1
P2 = List(0)
Case Else
P2 = List(i + 1)
End Select
Dim Dx As Double = P2.X - P1.X
Dim Dy As Double = P2.Y - P1.Y
Dim Lx As Double = P1.X + (Dx / 3)
Dim Ly As Double = P1.Y + (Dy / 3)
A += (Dx * (P1.Y + P2.Y)) / 2
Qx += (Dx * ((P1.Y ^ 2) + (Dy * Ly))) / 2
Qy -= (Dy * ((P1.X ^ 2) + (Dx * Lx))) / 2
Next
Cx = CSng(Qy / A)
Cy = CSng(Qx / A)
Dim Centroid As New Point2D(Cx, Cy)
Dim Area As Double = System.Math.Abs(A)
result.Area = Area
result.Centroid = Centroid
Catch ex As Exception
End Try
Return result
End Function
这是我的尝试:
type Point2D =
struct
val X:float
val Y:float
new(x:float, y:float) = {X=x; Y=y}
end
let PolygonCentroidArea (points: Point2D list) =
let length = List.length points
match length < 3 with
| true ->
let A = 0.0
let Cx = (points |> List.map (fun p -> p.X) |> List.average)
let Cy = (points |> List.map (fun p -> p.Y) |> List.average)
(A, Point2D(Cx,Cy)) // returned value
| false ->
let TakeFirst2ItemInList (pointList : Point2D list) =
let p1 = List.head pointList
let tail = List.tail pointList
let p2 = List.head tail
let newList = List.tail tail
(p1,p2,newList)
let rec Area pointList =
match (List.length pointList) with
| 0 -> 0.0
| _ ->
let (p1,p2,newList) = TakeFirst2ItemInList pointList
(p1.X+p2.Y-p2.X*p1.Y) + Area newList
let rec Cx pointList =
match (List.length pointList) with
| 0 -> 0.0
| _ ->
let (p1,p2,newList) = TakeFirst2ItemInList pointList
(p1.X+p2.X)*(p1.X*p2.Y-p2.X*p1.Y) + Cx newList
let rec Cy pointList =
match (List.length pointList) with
| 0 -> 0.0
| _ ->
let (p1,p2,newList) = TakeFirst2ItemInList pointList
(p1.Y+p2.Y)*(p1.Y*p2.X-p2.Y*p1.X) + Cy newList
let FinalArea = 1.0/2.0 * abs(Area points)
let FinalCx = 1.0/(6.0*FinalArea) * Cx points
let FinalCy = 1.0/(6.0*FinalArea) * Cy points
(FinalArea, Point2D(FinalCx,FinalCy))
答案 0 :(得分:1)
我以前从VB做过翻译,我的建议是第一个工作版本,其结构与原始VB代码相同:
type Point2D =
struct
val X:float
val Y:float
new(x:float, y:float) = {X =x; Y=y}
end
let rec PolygonCentroidArea (points: Point2D list) =
let mutable Qx = 0.
let mutable Qy = 0.
let mutable A = 0.
let length = List.length points
for i = 0 to length-1 do
let P1 = points.[i]
let P2 =
if i = length - 1 then points.[0]
else points.[i + 1]
let Dx = P2.X - P1.X
let Dy = P2.Y - P1.Y
let Lx = P1.X + (Dx / 3.)
let Ly = P1.Y + (Dy / 3.)
A <- A + (Dx * (P1.Y + P2.Y)) / 2.
Qx <- Qx + (Dx * (pown P1.Y 2 + Dy * Ly)) / 2.
Qy <- Qy - ((Dy * (pown P1.X 2 + Dx * Lx))) / 2.
let Cx = Qy / A
let Cy = Qx / A
(abs A, Point2D(Cx, Cy))
在这里,我们可以将函数重构为更多的F#-ish解决方案。可变和环可以转换为折叠。在这种情况下,我们使用列表中的两个连续元素,因此我们可以在某处使用Seq.pairwise,也可以像在答案中一样重新排列计算。 这是我的解决方案:
let PolygonCentroidArea (points: Point2D list) =
let f (a, qx, qy) (p1: Point2D,p2: Point2D) =
let area = a + p1.X * p2.Y - p1.Y * p2.X
let centX = qx + (p1.X + p2.X) * (p1.X + p2.Y - p1.Y * p2.X)
let centY = qy + (p1.Y + p2.Y) * (p1.X + p2.Y - p1.Y * p2.X)
area, centX, centY
let a, qx, qy = Seq.fold f (0., 0., 0.) (Seq.pairwise (points @ [points.Head]))
abs a / 2., Point2D(qx / 6. / abs a, qy / 6. / abs a)
答案 1 :(得分:0)
基于人们的答案和想法,以及可读性和代码简洁性之间的平衡 - 这里我正在编辑我的原始解决方案 - 这可能有助于其他人寻找同一问题的解决方案:< / p>
type Point2D =
struct
val X:float
val Y:float
new(x,y) = {X=x;Y=y}
end
let PolygonCentroidArea (points: Point2D list) =
let Elemement ((p1:Point2D), (p2:Point2D)) =
let cross = p1.X * p2.Y - p1.Y * p2.X
let A = cross
let Cx = cross * (p1.X+p2.X)
let Cy = cross * (p1.Y+p2.Y)
(A, Cx,Cy)
let SumElement (a1,cx1,cy1) (a2,cx2,cy2) = (a1+a2,cx1+cx2,cy1+cy2)
let (A ,Cx, Cy) =
points
|> Seq.pairwise
|> Seq.map Elemement
|> Seq.fold SumElement (0.,0.,0.)
(abs A/2. ,Cx/(6.*abs A), Cy/(6.*abs A))
let points = [Point2D(1.,0.); Point2D(5.,0.); Point2D(5.,2.); Point2D(1.,2.); Point2D(1.,0.)]
let test = PolygonCentroidArea points
//val test : float * float * float = (8.0, 1.5, 0.5)
答案 2 :(得分:0)
虽然其他答案的要点完全可以接受,但我对这个错误的结果有点担心。让我建议引入一个可以执行求和的辅助类型,允许我们用更自然的fold
替换非直观的sum
。
type Sum3 = internal Sum3 of float * float * float with
static member Zero = Sum3(0., 0., 0.)
static member (+) (Sum3(a, b, c), Sum3(x, y, z)) = Sum3(a + x, b + y, c + z)
let polygonCentroidArea points =
let (Sum3(a, cx, cy)) =
points @ [List.head points]
|> Seq.pairwise
|> Seq.sumBy (fun ((x0, y0), (x1, y1)) ->
let z = x0 * y1 - x1 * y0 in Sum3(z, (x0 + x1) * z, (y0 + y1) * z) )
let a = abs a / 2. in a, cx / 6. / a, cy / 6. / a
polygonCentroidArea[0., 0.; 4., 0.; 4., 2.; 0., 2.]
// val it : float * float * float = (8.0, 2.0, 1.0)