在F#中,你如何处理(float seq)和{ts:DateTime; value:float}以通用方式?

Blu*_*rin 6 f# coding-style

编写函数的最佳方法是什么,以便它们可以无缝地处理:

  • 一个浮点seq
  • 时间戳系列的类型{ts:DateTime; value:float} seq,其中值在浮点数中称为value

我特别需要编写函数,例如计算时间序列的平均值/方差/随机变换,我想只编写这些函数的1个版本.

Tom*_*cek 13

正如评论中所述,关键问题是您希望在数据类型上编写什么类型的操作.有可能为基本的数值运算执行此操作,但如果您需要一些更复杂的计算,那么编写两个单独的函数可能会更容易.

无论如何,如果要使用基本的数值运算,则需要为时间戳类型定义标准数字运算符.我描述了在最近的一篇文章这样.以下实现+,除以整数和零:

open System

type TimedFloat = 
  { Time : DateTime
    Value : float }
  static member (+) (tf1, tf2) = 
    { Time = DateTime(tf1.Time.Ticks + tf2.Time.Ticks)
      Value = tf1.Value + tf2.Value }
  static member Zero = 
    { Time = DateTime.MinValue
      Value = 0.0 } 
  static member DivideByInt(tf, n) =
    { Time = DateTime(tf.Time.Ticks / int64 n)
      Value = tf.Value / float n }
Run Code Online (Sandbox Code Playgroud)

+运营商有点怀疑,因为你有一些非常大的日子结束了(也许是使用TimeSpan从上会更有意义).但是,一旦定义了运算符,您就可以使用Seq.average:

[ { Time = DateTime.Now
    Value = 3.0 }
  { Time = DateTime.Now.AddDays(2.0)
    Value = 10.0 } ]
|> Seq.average
Run Code Online (Sandbox Code Playgroud)

Seq.average函数适用于两种类型,因为它是使用静态成员约束编写的(这意味着它适用于具有必要成员的任何类型).编写这样的函数比编写普通函数更困难,所以默认情况下我可能不会使用这种函数.无论如何,这里有一个介绍更多的例子,这个答案显示更多有用的技巧.

编辑 -正如Jon Harrop指出的那样,这是一种非常复杂的方法,它只给你带来有限的好处.如果您只需要处理这些值,那么转换为一系列值是一种更好的方法.如果您需要更复杂的计算,那么我认为编写泛型函数并不值得.为float和timestamped值编写两个单独的函数可能会更容易.


Jon*_*rop 9

只需将带时间戳的seq转换为float seq,如下所示:

xs
|> Seq.map (fun x -> x.value)
Run Code Online (Sandbox Code Playgroud)

  • 如果您只需要计算值,那么这就是要走的路.但显然,如果您需要考虑时间戳(例如,插入您需要时间的值),它就不起作用.这就是我的答案显示更复杂的方法的原因. (3认同)