Моя первая F # программа - PullRequest
1 голос
/ 21 мая 2010

Я только что закончил писать свою первую программу на 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 ]

6 голосов
/ 21 мая 2010

В общем, вы должны стараться использовать неизменяемые типы данных и избегать императивных конструкций, таких как глобальные переменные и императивные циклы - хотя во многих случаях их использование в 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
3 голосов
/ 21 мая 2010

Просто пара мыслей ...

Вы, вероятно, хотите использовать 'use', а не 'let' в нескольких местах, так как я думаю, что некоторые объекты в программе IDisposable.

Вы можете обернуть метод EstablishConnection и заключительный цикл while в асинхронные блоки (и внести другие незначительные изменения), например, так: вы можете ожидать асинхронного соединения без блокировки потока.

1 голос
/ 21 мая 2010

На первый взгляд это написано в императивном стиле, а не в функциональном стиле, что имеет смысл, учитывая, что большая часть программы включает в себя побочные эффекты (т.е. ввод / вывод). Строка за строкой, это почти похоже на программу на C #.

Учитывая количество операций ввода-вывода, я не знаю, сколько вы можете сделать с этой конкретной программой, чтобы сделать ее более функциональным стилем кодирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...