F# 遍历集合并构建列表

标签 f# iteration

我是一名 .Net 开发人员,但对 F# 和函数式编程很陌生。有人可以为我指出以下问题的正确方向:

我正在尝试遍历从 CSV 读取的一系列数据并构建一种汇总列表。伪代码是

type record = { Name:string; Time:DateTime;}

type summary = {Name:String; Start:DateTime; End:DateTime}

示例数据: (姓名时间)

  • 10:01
  • 10:02
  • 10:03
  • 早 11:15
  • 早 11:25
  • 早 11:30
  • 中点 12:00
  • 13:01
  • 13:05

我正在尝试遍历序列并构建第二个序列:

Seq<Summary>

(名称开始结束)

  • 10:01 10:03
  • 早 11:15 11:30
  • 中 12:00 12:00
  • 13:01 13:05

我应该给 seq<record> 配管吗?到一个在 foreach 中迭代的函数风格,还是有更好的方法?我已经在 F# 中对数据进行了排序,因此数据按时间顺序排列。我不必担心它们会出现故障。

如果是 C#,我可能会执行类似(伪代码)的操作:

List<Summary> summaryData

foreach(var r in records)
{
   Summary last =  summaryData.LastOrDefault()

   if(last == null)
   {
      summaryData.add( new Summary from r)
   }
   else
   {
     if(last.Name = r.Name)
     {
           last.End = r.Time
     } 
     else
     {
           summaryData.add( new Summary from r)
     }

} 

非常感谢任何帮助!

最佳答案

除了声明性之外,函数式编程还包括抽象具体类型的基本要求的能力,即泛型编程。只要满足要求(F# 将它们称为约束),您的算法就会以通用方式适用,而与具体的数据结构无关。

正如您的问题描述中所述,您可能有一个序列(对象有序集合的最通用数据结构),可以从中提取 key 。这些键应进行不等式测试,因此存在等式约束。依靠序列的顺序,任何东西都不受约束。表示为 F# 签名,您的输入数据由 source:seq<'T> 描述和关键投影函数为projection:('T -> 'Key) when 'Key : equality .

作为一个完整的函数签名,我建议projection:('T -> 'Key) -> source:seq<'T> -> seq<'T * 'T> when 'Key : equality .返回一系列对避免引入额外的类型参数。它匹配输入,除了一些选择性的重新排列。这是此功能的可能实现,没有要求效率甚至正确性。请注意,'Key 上的等式约束是推断出来的,从未明确说明过。

let whenKeyChanges (projection : 'T -> 'Key) (source : seq<'T>) =
    // Wrap in option to mark start and end of sequence
    // and compute value of every key once
    seq{ yield None
         yield! Seq.map (fun x -> Some(x, projection x)) source
         yield None }
    // Create tuples of adjacent elements in order to
    // test their keys for inequality
    |> Seq.pairwise
    // Project to singleton in case of the first and the
    // last element of the sequence, or to a two-element
    // sequence if keys are not equal; concatenate the
    // results to obtain a flat sequence again
    |> Seq.collect (function
        | None, Some x | Some x, None -> [x]
        | Some(_, kx as x), Some(_, ky as y)
            when kx <> ky -> [x; y]
        | _ -> [] )
    // Create tuples of adjacent elements a second time.
    |> Seq.pairwise
    // Only the first and then every other pair will contain
    // indentical keys
    |> Seq.choose (fun ((x, kx), (y, ky)) ->
        if kx = ky then Some(x, y) else None )

混凝土上的示例应用程序(X * string) list ,关键是 X。它在您的 seq<record> 上也同样有效。当 (fun r -> r.Name) 获取 key 时.

type X = A | B | C
[   A, "10:01"
    A, "10:02"
    A, "10:03"
    B, "11:15"
    B, "11:25"
    B, "11:30"
    C, "12:00"
    A, "13:01"
    A, "13:05" ] |> whenKeyChanges fst
// val it : seq<(X * string) * (X * string)> =
//   seq
//     [((A, "10:01"), (A, "10:03")); ((B, "11:15"), (B, "11:30"));
//      ((C, "12:00"), (C, "12:00")); ((A, "13:01"), (A, "13:05"))]

关于F# 遍历集合并构建列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31595184/

相关文章:

c++ - 如何最好地控制迭代方向?

Java - 遍历 ArrayList 行(列)?

f# - 使用 Accord.net 获取数据点到其质心的距离

struct - 改变 F# [<Struct>] 记录

F#/C# - fsx 脚本文件和项目引用

.net - F#度量单位如何工作?

ruby-on-rails - Ruby Do 循环和迭代

javascript - 如何以懒惰的方式调用jquery的.find?

f# - API速率限制器间歇性挂起

java - 在 for 循环中重命名文件时出错