2014-01-23 179 views
2

我想为已经绘制到窗体的2D对象创建一个保存/加载函数。从字符串数组解析值

type circle = { X : int; Y : int; Diameter : int; Brush : Brush} 
type Square = { X : int; Y : int; Length : int; Height: int; Brush : Brush} 

当我创建对象时,我把它们放到2个列表中,每个类型1个。 我最初的想法是读写这些对象的文本文件,见下图:

saveFile.Click.Add(fun _ -> 
for c in listOfCircles do 
    myfile.WriteLine("Circle," + c.X.ToString() + "," + c.Y.ToString() + "," + c.Diameter.ToString() + "," + c.Brush.ToString()) 
for s in listOfSquares do 
    myfile.WriteLine("Square," + s.X.ToString() + "," + s.Y.ToString() + "," + s.Height.ToString() + "," + s.Length.ToString() + "," + s.Brush.ToString()) 
myfile.Close() // close the file 

而且在文本文件,它看起来像这样

Circle,200,200,50,System.Drawing.SolidBrush 
Square,50,55,45,55,System.Drawing.SolidBrush 

在这里,我想读这些值,然后能够解析它们并通过添加列表中的对象并重新绘制它们来重新创建对象。

let readCircle = 
    System.IO.File.ReadAllLines path 
    |> Array.choose (fun s -> 
    match s.Split ',' with 
    | [|x; y ; z ; b ; _|] when x = "Circle" -> Some (y, z, b) 
    | _ -> None) 

let readSquare = 
    System.IO.File.ReadAllLines path 
    |> Array.choose (fun s -> 
    match s.Split ',' with 
    | [|x; y ; z ; b ; a ; _|] when x = "Square" -> Some (y, z, b, a) 
    | _ -> None) 

这些功能给了我

val readCircle : (string * string * string) [] = [|("200", "200", "50")|] 
val readSquare : (string * string * string * string) [] = [|("50", "55", "45", "55")|] 

,我现在是林不知道如何从阵列获取的值的问题。下面是多个圈子的例子。

val readCircle : (string * string * string) [] = [|("200", "200", "50"); ("200", "200","50")|] 

有关如何从这里/如何解决这个问题的任何想法或意见是非常感激!问题总结:我怎么能拿从数组中的值,并把它们在例如我已经创建添加功能,见下图:

listOfCircles.Add({ X = 200; Y = 200; Diameter = 50; Brush = Brushes.Black}) 
+0

我不确定我是否理解这个问题。但是,假设您需要将值数组转换为形状列表,则可以使用'values |> Seq.map createCircle |> Seq.toList'。 – Daniel

+0

我想从数组中获取值并将它们放入listOfCircles.Add函数中,请参阅编辑后的底部。 – Bobson

回答

2

你可以转换你使用的字符串元组数组Array.map,例如

[|("200", "200", "50"); ("200", "200","50")|] 
|> Array.map (fun (x,y,d) -> {X = int32 x; Y = int32 y; Diameter = int32 d; Brush = Brushes.Black}) 

如果转换为circlesquare为您解析的文件,那么你将有circle数组或square数组,你可以直接添加到您的列表中它可能是一个有点清晰。

let readCircle = 
    System.IO.File.ReadAllLines path 
    |> Array.choose (fun s -> 
    match s.Split ',' with 
    | [|t; x; y; d; _|] when t = "Circle" -> 
     Some {X = int32 x; Y = int32 y; Diameter = int32 d; Brush = Brushes.Red} 
    | _ -> None) 

但是......如果你想做出较大的改动,你可以使用歧视工会来代表你的形状,他们会再分享Shape一个常见的类型,你可以在同一个函数解析圆形和正方形。

type Shape = 
| Circle of X : int * Y : int * Diameter : int * Brush : Brush 
| Square of X : int * Y : int * Length : int * Height: int * Brush : Brush 

let readShapes (data: string array) = 
    data 
    |> Array.choose (fun s -> 
    match s.Split ',' with 
    | [|t; x; y; d; _|] when t = "Circle" -> 
     Some (Circle(X = int32 x, Y = int32 y, Diameter = int32 d, Brush = Brushes.Red)) 
    | [|t; x; y; l; h; _|] when t = "Square" -> 
     Some (Square(X = int32 x, Y = int32 y, Length = int32 l, Height = int32 h, Brush = Brushes.Red)) 
    | _ -> None) 

let listOfShapes = ResizeArray<_>() 

let testInput = """ 
Circle,200,200,50,System.Drawing.SolidBrush 
Square,50,55,45,55,System.Drawing.SolidBrush""" 

testInput.Split('\n') // System.IO.File.ReadAllLines path 
|> readShapes 
|> Array.iter (listOfShapes.Add) 

这将导致

val it : System.Collections.Generic.List<Shape> = 
    seq 
    [Circle (200,200,50,System.Drawing.SolidBrush {Color = Color [Red];}); 
    Square (50,55,45,55,System.Drawing.SolidBrush {Color = Color [Red];})] 

然后,您可以使用模式匹配绘制每种形状

let drawShape shape = 
    match shape with 
    | Circle(x,y,d,b) -> 
     printfn "Pretend I just drew a circle at %d,%d with diameter %d." x y d 
    | Square(x,y,l,h,b) -> 
     printfn "Pretend I just drew a rectangle at %d,%d that was %d long and %d high." x y l h 

listOfShapes |> Seq.iter drawShape 

给予

Pretend I just drew a circle at 200,200 with diameter 50. 
Pretend I just drew a rectangle at 50,55 that was 45 long and 55 high. 
+0

谢谢!我从中学到了很多。 – Bobson

2

如果我理解你的目标,这是我会怎么做呢。我只实施了Circle;您需要修改它以处理Square

open System 
open System.Collections.Generic 
open System.Drawing 
open System.IO 

let memoize f = 
    let cache = Dictionary() 
    fun key -> 
    match cache.TryGetValue(key) with 
    | true, value -> value 
    | _ -> 
     let value = f key 
     cache.Add(key, value) 
     value 

let getBrush = 
    memoize (fun name -> typeof<Brushes>.GetProperty(name).GetValue(null) :?> SolidBrush) 

type Circle = 
    { X : int 
    Y : int 
    Diameter : int 
    Brush : SolidBrush } with 
    override this.ToString() = 
    sprintf "Circle,%d,%d,%d,%s" this.X this.Y this.Diameter this.Brush.Color.Name 
    static member Parse(s: string) = 
    match s.Split(',') with 
    | [|"Circle";x;y;diameter;brushName|] -> {X=int x; Y=int y; Diameter=int diameter; Brush=getBrush brushName} 
    | _ -> invalidArg "s" "Cannot parse string" 

let writeShapesToFile fileName shapes = 
    File.WriteAllLines(fileName, Seq.map (sprintf "%O") shapes) 

let readShapesFromFile fileName = 
    File.ReadAllLines(fileName) |> Array.map Circle.Parse 

此外,你可能会考虑,因为很多CircleSquare共享结构和行为的使用类层次结构,而不是记录。

1

这是有趣的 - 我接近完全不同嗯,比丹尼尔的方式(但我同意他说,你的班级可能是你的形状更好的方法)。相反,我趁着歧视工会的(也有更好的方法来做到这一点 - 更晚):

首先,我定义的参数列表的类型用于制造形状:

type Parameter = 
    | Label of string 
    | Number of int 

现在让我们将字符串转换为一个参数:

let toParameter s = 
    match Int32.TryParse(s) with 
    | (true, i) -> Number(i) 
    | (_, _) -> Label(s) 

我们的字符串列表转换为参数列表:

let stringListToParameterList stringlist = stringlist |> List.map(function s -> toParameter s) 

我们以逗号分隔的字符串转换为字符串列表:

let commastringToList (s:string) = s.Split(',') |> Array.toList 

OK - 伟大的 - 让我们来定义你的记录和主形状:

type circlerec = { X : int; Y : int; Diameter : int; Brush : Brush} 
type squarerec = { X : int; Y : int; Length : int; Height: int; Brush : Brush} 
type Shape = 
    | Circle of circlerec 
    | Square of squarerec 

有了这一点,我们需要一种方法来从参数列表中创建一个Shape。这是蛮力,但它读取不够好:

​​

这是密集的,但我使用F#的模式匹配来找出每个形状的各种参数。请注意,您现在可以在文件中执行类似Square,x,y,size,colorName的事情,并通过在模式中添加长度和高度等于大小的Square来创建Square。

最后是主菜,你的文件转换成形状:

let toShapes path = 
    System.IO.File.ReadAllLines path |> Array.toList |> 
     List.map(function s -> s |> commastringToList |> 
     stringListToParameterList |> toShape) 

该文件以字符串列表,然后每一行的形状映射在每行映射,但管道逗号串到列表转换器,然后通过参数列表,然后到一个形状。

现在,这是不好的是,错误检查是非常可怕的,并且参数类型应该包括Pigment of Color,这将允许您查看进来的字符串,如果它是有效的颜色名称,映射到一个颜料还有一个标签。