dgp
dgp

Reputation: 185

F# Conversion from tuple to enumerated list

The code below stems from work on a Euclidean Distance algorithm. The color table was simply a vehicle to test the algorithm. It is perhaps reinventing the wheel, however it is useful in itself. Any 3 RGB integers (0-255) can be associated with the nearest X11 color name. Many thanks to svick for his insights.

In the current code, the ColorTable is initialized via the AddColor method after the instance is created. However, the loadrgb/colorinfo combination can be used to pull down an X11 color table off the web.

I'm having one last problem in initializing the color table from the online version of the X11 rgb.txt file. I need to parse the text into a {Name: Values:} list. Currently, the results are in a tuple of strings. I'm working to have "colorinfo" load the "ColorTable".

// currently the color table is create via the AddColor method, however
// the initial values should be created with the loadrgb and colorinfo members
type MyFSColorTable() = 

// pull the X11 rgb.txt color table off the web in text format
  static let loadrgb =
    let url = "http://people.csail.mit.edu/jaffer/Color/rgb.txt"
    let req = WebRequest.Create(url)
    let resp = req.GetResponse()
    let stream = resp.GetResponseStream()
    let reader = new StreamReader(stream)
    let txt = reader.ReadToEnd()
    txt

// parse the text of the rgb.txt color table into a Name: Values: list
  static let colorinfo =
      loadrgb.Split([|'\n'|])
      |> Seq.skip 1
      |> Seq.map (fun line -> line.Split([|'\t'|]))        
      |> Seq.filter (fun values -> values |> Seq.length = 3)
      |> Seq.map (fun values -> string values.[0], string values.[2])
      |> Seq.map (fun (rgb, name) -> rgb.Split([|' '|]), name)
      |> Seq.map (fun (rgb, name) -> [|name, rgb.[0], rgb.[1], rgb.[2]|])

  // Mutable Color Table will be defined on-the-fly
  let mutable ColorTable = []
  // Euclidean distance between 2 vectors - float is overkill here
  static let Dist (V1: float[]) V2 =
    Array.zip V1 V2
      |> Array.map (fun (v1, v2) -> pown (v1 - v2) 2)
      |> Array.sum

//    Add new colors to the head of the ColorTable
  member x.AddColor name rgb = ColorTable <- {Name = name; Values = rgb}::ColorTable

//    Find nearest color by calculating euclidean distance of all colors, 
//    then calling List.minBy for the smallest 
  member x.FindNearestColor (rgb : float[]) =
    let nearestColor =
      ColorTable |> List.minBy (fun color -> Dist rgb color.Values)
    nearestColor.Name

Upvotes: 0

Views: 470

Answers (2)

Daniel
Daniel

Reputation: 47904

You can shorten your code, and return {Name: string; Values: float[]} list as desired, with the following:

static let colorinfo =
  loadrgb.Split('\n')
    |> Seq.skip 1
    |> Seq.choose (fun line ->
      match line.Split('\t') with
      | [|rgb; _; name|] ->
        let values = rgb.Split(' ') |> Array.map float
        Some({Name=name; Values=values})
      | _ -> None)
    |> Seq.toList

Upvotes: 0

Tomas Petricek
Tomas Petricek

Reputation: 243061

At the moment, your code that constructs colorinfo produces a sequence containing an array with just a single element which is a tuple (containing four strings). This means that the type of the overall result is - in your current version - seq<(string * string * string * string) []>:

  (...)
  |> Seq.map (fun (rgb, name) -> 
       [|name, rgb.[0], rgb.[1], rgb.[2] |]

This is probably not intended - if you wanted to create an array with four strings, you need to use semicolon instead of comma [| name; rgb.[0]; ... |] and if you wanted to create four-element tuple, then you can just omit the [| and |] around the tuple.

There is no way to automatically turn array or a tuple into a named record type (assuming you have a record with Name:string and Values:float[]), so the best option is to do this transformation in the last step of your pipeline. You can replace the above with:

  |> Seq.map (fun (rgb, name) -> 
        { Name = name
          Values = [| float rgb.[0]; float rgb.[1]; float rgb.[2] |] })
  |> List.ofSeq

I also added List.ofSeq to the end of the snippet so that you get a list back - that is the same type you're using for ColorTable at the moment.

(Alternatively, you could say Values = Array.map float rgb, which converts all strings in rgb to floats and works for any lenght of the array.)

Upvotes: 2

Related Questions