我的第一个F#计划

时间:2010-05-20 20:28:04

标签: f#

我刚刚写完第一个F#程序。功能明智的代码以我想要的方式工作,但不确定代码是否有效。如果有人可以为我查看代码并指出可以改进代码的区域,我将不胜感激。

由于 Sudaly

open System
open System.IO
open System.IO.Pipes
open System.Text
open System.Collections.Generic
open System.Runtime.Serialization


[<DataContract>] 
type Quote = { 
    [<field: DataMember(Name="securityIdentifier") >] 
    RicCode:string
    [<field: DataMember(Name="madeOn") >] 
    MadeOn:DateTime
    [<field: DataMember(Name="closePrice") >] 
    Price:float 
    }

let m_cache = new Dictionary<string, Quote>() 

let ParseQuoteString (quoteString:string) = 
    let data = Encoding.Unicode.GetBytes(quoteString)
    let stream = new MemoryStream() 
    stream.Write(data, 0, data.Length); 
    stream.Position <- 0L 
    let ser = Json.DataContractJsonSerializer(typeof<Quote array>) 
    let results:Quote array = ser.ReadObject(stream) :?> Quote array
    results

let RefreshCache quoteList =
    m_cache.Clear()
    quoteList |> Array.iter(fun result->m_cache.Add(result.RicCode, result))


let EstablishConnection() =
    let pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut, 4)
    let mutable sr = null
    printfn "[F#] NamedPipeServerStream thread created, Wait for a client to connect"
    pipeServer.WaitForConnection()
    printfn "[F#] Client connected."
    try
        // Stream for the request. 
        sr <- new StreamReader(pipeServer)
    with
    | _ as e -> printfn "[F#]ERROR: %s" e.Message
    sr


while true do
    let sr = EstablishConnection()
    // Read request from the stream.
    printfn "[F#] Ready to Receive data"

    sr.ReadLine()  
    |>  ParseQuoteString  
    |>  RefreshCache

    printfn "[F#]Quot Size, %d" m_cache.Count
    let quot = m_cache.["MSFT.OQ"]
    printfn "[F#]RIC: %s" quot.RicCode
    printfn "[F#]MadeOn: %s" (String.Format("{0:T}",quot.MadeOn))
    printfn "[F#]Price: %f" quot.Price

3 个答案:

答案 0 :(得分:6)

通常,您应该尝试使用不可变数据类型并避免使用必要的构造,例如全局变量和命令式循环 - 尽管在F#中使用它们在许多情况下都很好,但只有在有充分理由这样做时才应该使用它们。以下是一些可以使用功能方法的示例:


首先,为了使代码更具功能性,您应该避免使用全局可变缓存。相反,您的RefreshCache函数应该返回数据作为结果(最好使用一些功能数据结构,例如F#Map类型):

let PopulateCache quoteList = 
  quoteList 
  // Generate a sequence of tuples containing key and value 
  |> Seq.map (fun result -> result.RicCode, result)
  // Turn the sequence into an F# immutable map (replacement for hashtable)
  |> Map.ofSeq

使用它的代码会改变如下:

let cache = 
  sr.ReadLine()   
  |>  ParseQuoteString   
  |>  PopulateCache

printfn "[F#]Quot Size, %d" m_cache.Count 
let quot = m_cache.["MSFT.OQ"] 
// The rest of the sample stays the same

EstablishConnection函数中,您绝对不需要声明可变变量sr,因为如果发生异常,函数将返回null。我会改为使用option类型来确保处理这种情况:

let EstablishConnection() = 
    let pipeServer = 
      new NamedPipeServerStream("testpipe", PipeDirection.InOut, 4) 
    printfn "[F#] NamedPipeServerStream thread created..." 
    pipeServer.WaitForConnection() 
    printfn "[F#] Client connected." 
    try // Wrap the result in 'Some' to denote success
        Some(new StreamReader(pipeServer))
    with e -> 
        printfn "[F#]ERROR: %s" e.Message 
        // Return 'None' to denote a failure
        None 

主循环可以使用递归函数编写,该函数在EstablishConnection失败时停止:

let rec loop() =
  match EstablishConnection() with
  | Some(conn) ->
      printfn "[F#] Ready to Receive data"
      // rest of the code
      loop() // continue looping
  | _ -> () // Quit

答案 1 :(得分:3)

只是几个想法......

你可能想在一些地方使用'use'而不是'let',因为我认为程序中的一些对象是IDisposable

您可以考虑将EstablishConnection方法和最终while循环包装在异步块中(并进行其他微小更改),以便例如您可以异步等待连接而不阻塞线程。

答案 2 :(得分:1)

乍一看,它是以命令式而不是功能性风格编写的,考虑到大部分程序都涉及副作用(即I / O),这确实有意义。换行,它几乎就像一个C#程序。

鉴于正在发生的I / O数量,我不知道您可以对此特定程序做多少工作,使其更具功能性的编码风格。