我刚刚写完第一个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
答案 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数量,我不知道您可以对此特定程序做多少工作,使其更具功能性的编码风格。