シームレスに処理できるように関数を作成する最良の方法は何ですか?
- float seq
- タイプ { ts : DateTime; のタイムスタンプ付きシリーズ。value: float } seq、値は value と呼ばれる float にあります
特に、時系列の平均/分散/ランダム変換の計算などの関数を作成する必要があり、これらの関数の 1 つのバージョンのみを作成したいと考えています。
シームレスに処理できるように関数を作成する最良の方法は何ですか?
特に、時系列の平均/分散/ランダム変換の計算などの関数を作成する必要があり、これらの関数の 1 つのバージョンのみを作成したいと考えています。
コメントで尋ねられたように、重要な質問は、データ型を上書きする操作の種類です。基本的な数値演算にはこれを実行できるかもしれませんが、より複雑な計算が必要な場合は、おそらく 2 つの別個の関数を作成する方が簡単です。
いずれにせよ、基本的な数値演算を使用したい場合は、タイムスタンプ付きの型に標準の数値演算子を定義する必要があります。これについては、最近の記事で説明しました。+
以下は、整数とゼロによる除算を実装します。
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 }
非常に大きな日付になってしまうため、+
オペレーターは少し疑わしいです (おそらくTimeSpan
最後から使用する方が理にかなっています)。ただし、演算子を定義すると、たとえば次のように使用できますSeq.average
。
[ { Time = DateTime.Now
Value = 3.0 }
{ Time = DateTime.Now.AddDays(2.0)
Value = 10.0 } ]
|> Seq.average
このSeq.average
関数は、静的メンバー制約を使用して記述されているため、両方の型で機能します (必要なメンバーを持つ任意の型で機能することを意味します)。このような関数を書くのは、通常の関数を書くより難しいので、デフォルトではおそらくこのスタイルを使用しないでしょう。とにかく、これはより多くの例を含む紹介であり、このSOの回答はより便利なトリックを示しています。
編集 - Jon Harrop が指摘しているように、これは非常に複雑なアプローチであり、限られた利点しか得られません。値を操作する必要があるだけの場合は、一連の値に変換することをお勧めします。より複雑な計算が必要な場合は、汎用関数を作成する価値はないと思います。float 値とタイムスタンプ付き値の 2 つの別個の関数を作成する方がおそらく簡単です。
タイムスタンプ付きのseqを次のようにfloatseqに変換するだけです。
xs
|> Seq.map (fun x -> x.value)