FP - 简洁且“好”代码

发布于 2024-11-05 15:04:47 字数 1295 浏览 1 评论 0原文

在大多数情况下,用 F# 编写代码会产生非常浓缩的直观工作。这段代码对我来说看起来有些必要且不方便。

  • 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 创建长度为 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

我正在寻找更好、更短或更简单的解决方案。当然,每一个有用的帖子都会被点赞:)

Writing code in F# in most cases results in very condense an intuitive work. This piece of code looks somehow imperative and inconvenient to me.

  • times is an array of float values

Lines inside the file times.csv always look like that:

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 generates an average of the values, dropping the lowest and highest time
  • getAllSubsetsOfLengthN creates a sequence of all consecutive subsets of length n. Is there a 'nicer' solution to that? Or does already exist something like that inside the F# core?
  • bestAverageOfN finds the lowest average of all the subsets

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

What I am looking for are nicer, shorter or easier solutions. Every useful post will be upvoted, of course :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

衣神在巴黎 2024-11-12 15:04:47

我想, getAllSubsetsOfLengthN 可以替换为 Seq.windowed

所以 bestAverageOfN 看起来像:

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

I guess, getAllSubsetsOfLengthN can be replaced with Seq.windowed

so bestAverageOfN will look like:

let bestAverageOfN n =
    times
    |> Seq.windowed n
    |> Seq.map average
    |> Seq.min
软糯酥胸 2024-11-12 15:04:47

无需太多思考,您就可以进行一些基本的功能重构。例如,在bestAverageOfN的计算中,您可以使用函数组合:

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

除了这个和desco的建议之外,我认为没有什么可以改变的。如果您不在代码中的任何地方使用特殊的 average 函数,您可以将其内联编写为 lambda 函数,但这实际上取决于您的个人喜好。

出于一般性的考虑,我可能会将 times 作为 bestAverageOfN 的参数:

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

Without much thinking, there are some basic functional refactorings you can make. For example, in the calculation of bestAverageOfN, you can use function composition:

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

Other than this and the suggestion by desco, I don't think there is anything I would change. If you don't use your special average function anywhere in the code, you could write it inline as a lambda function, but that really depends on your personal preferences.

Just for the sake of generality, I would probably make times an argument of bestAverageOfN:

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
ㄖ落Θ余辉 2024-11-12 15:04:47

既然您提到了用于解析输入的正则表达式,我想我应该向您展示这样一个解决方案。这可能有点过头了,但它也是一个更实用的解决方案,因为正则表达式是声明性的,而子字符串的内容则更具命令性。正则表达式也很好,因为如果输入的结构发生变化,索引子字符串的内容可能会变得混乱,那么它更容易增长,我会尝试完全避免它。

首先是几个活动模式,

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)

Since you mentioned regex for parsing your input, I thought I'd show you such a solution. It may well be overkill, but it is also a more functional solution since regular expressions are declarative while substring stuff is more imperative. Regex is also nice since it is easier to grow if the structure of your input changes, index substring stuff can get messy, and I try to avoid it completely.

First a couple active patterns,

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

Adopting @ildjarn's times implementation:

let times =
    File.ReadAllLines "times.csv"
    |> Array.map (function Groups @",.*?:(.*)$" [Float(value)] -> value)
小忆控 2024-11-12 15:04:47

由于 bestAversageOfN 已经被介绍,这里有一个 times 的替代实现:

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

Since bestAversageOfN has already been covered, here's an alternative implementation of times:

let times =
    File.ReadAllLines "times.csv"
    |> Array.map (fun l -> l.LastIndexOf ':' |> (+) 1 |> l.Substring |> float)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文