2011-05-07 58 views
1

在大多數情況下在F#中編寫代碼會導致非常簡明直觀的工作。這段代碼看起來有點迫切和不方便。FP - 濃縮和'好'代碼

  • times是浮子的值的數組

文件times.csv內部行總是看起來像:

Mai 06 2011 05:43:45 nachm.,00:22.99 
Mai 04 2011 08:59:12 nachm.,00:22.73 
Mai 04 2011 08:58:27 nachm.,00:19.38 
Mai 04 2011 08:57:54 nachm.,00:18.00 
  • average生成值的平均值,滴最低和最高時間
  • getAllSubsetsOfLengthN創建一個sequ所有連續子集的長度爲n。有沒有更好的解決方案?或者在F#內核中已經存在類似的東西?
  • bestAverageOfN找到最低的平均所有子集

let times = 
    File.ReadAllLines "times.csv" 
    |> Array.map (fun l -> float (l.Substring((l.LastIndexOf ':') + 1))) 
let average set = 
    (Array.sum set - Array.min set - Array.max set)/float (set.Length - 2) 
let getAllSubsetsOfLengthN n (set:float list) = 
    seq { for i in [0 .. set.Length - n] -> set 
              |> Seq.skip i 
              |> Seq.take n } 
let bestAverageOfN n = 
    times 
    |> Array.toList 
    |> getAllSubsetsOfLengthN n 
    |> Seq.map (fun t -> t 
         |> Seq.toArray 
         |> average) 
    |> Seq.min 

的什麼我找的是更好的,更短的或更容易的解決方案。當然,每個有用的帖子都會被upvoted :)

回答

3

沒有太多的思考,你可以做一些基本的功能重構。例如,在bestAverageOfN計算,你可以使用函數組合:

let bestAverageOfN n = 
    times 
    |> Array.toList 
    |> getAllSubsetsOfLengthN n 
    |> Seq.map (Seq.toArray >> average) 
    |> Seq.min 

除此以外和建議通過德斯科,我不認爲這有什麼我會改變。如果你在代碼中的任何地方都沒有使用你的特殊average函數,你可以把它作爲lambda函數內聯編寫,但這取決於你的個人偏好。

只是爲了一般性起見,我很可能會令timesbestAverageOfN參數:

let bestAverageOfN n times = 
    times 
    |> Seq.windowed n 
    |> Seq.map (fun set -> 
      (Array.sum set - Array.min set - Array.max set)/float (set.Length - 2)) 
    |> Seq.min 
+0

這幾乎涵蓋了一切。很高興知道'Seq.windowed'。非常感謝! – fjdumont 2011-05-07 12:36:47

4

我想,getAllSubsetsOfLengthN可以Seq.windowed

更換

所以bestAverageOfN的樣子:

let bestAverageOfN n = 
    times 
    |> Seq.windowed n 
    |> Seq.map average 
    |> Seq.min 
1

由於bestAversageOfN已經被覆蓋,所以這裏是的替代實施方式:

let times = 
    File.ReadAllLines "times.csv" 
    |> Array.map (fun l -> l.LastIndexOf ':' |> (+) 1 |> l.Substring |> float) 
+0

當我得到這個正確的,'(+)'是一個函數,而不是一個運算符?另外,在這種情況下,你會認爲正則表達式是一個過度的東西嗎? – fjdumont 2011-05-07 12:39:37

+0

@fjdumont:更重要的是,'+'是一個帶curix的二元函數,它具有中綴語義,這就是我們認爲的「操作符」。 '(+)'僅僅是作爲函數直接捕獲它的方式。 – ildjarn 2011-05-07 21:21:03

+0

@fjdumont:是的,我個人認爲正則表達式會過量以避免簡單的'LastIndexOf'調用。這並不是說我不是普通的正規愛好者,恰恰相反;只是在*這個特殊情況下*它似乎並沒有增加任何價值。 – ildjarn 2011-05-07 21:23:27

2

既然你提到解析輸入的正則表達式,我想我會告訴你這樣的解決方案。它可能是矯枉過正,但它也是一個更實用的解決方案,因爲正則表達式是聲明性的,而子串的東西更加必要。正則表達式也很好,因爲如果輸入結構發生變化,索引子字符串可能會變得混亂,並且我試圖完全避免它,它會更容易增長。

首先一對夫婦主動模式,

open System.Text.RegularExpressions 
let (|Groups|_|) pattern input = 
    let m = Regex.Match(input, pattern) 
    if m.Success then 
     Some([for g in m.Groups -> g.Value] |> List.tail) 
    else 
     None 

open System 
let (|Float|_|) input = 
    match Double.TryParse(input) with 
    | true, value -> Some(value) 
    | _ -> None 

採用@ ildjarn的times實現:

let times = 
    File.ReadAllLines "times.csv" 
    |> Array.map (function Groups @",.*?:(.*)$" [Float(value)] -> value) 
+0

+1這使得解析更可讀。或者,你也可以創建一些聰明的選擇子字符串的活動模式。 – 2011-05-07 16:30:18

+0

儘管表達式忽略了小數字,但它看起來功能完好。儘管如此,還有幾行代碼。我非常喜歡這個解決方案,可能會學到一些活躍的模式:p謝謝! – fjdumont 2011-05-07 17:50:29