3

シーケンスで機能する一連の関数があり、それらを次のように一緒に使用したいとします。

let meanAndStandardDeviation data = 
    let m = mean data
    let sd = standardDeviation data
    (m, sd)

上記のコードは、シーケンスを2回列挙します。同じ結果が得られるが、シーケンスを1回だけ列挙する関数に興味があります。この関数は次のようになります。

magicFunction (mean, standardDeviation) data

ここで、入力は関数とシーケンスのタプルであり、出力は上記の関数と同じです。

関数meanstadardDeviationがブラックボックスであり、それらの実装を変更できない場合、これは可能ですか?

私が自分で書いた場合meanstandardDeviationそれらを一緒に機能させる方法はありますか?たぶん、どういうわけか、次の関数への入力を生成し続け、完了したら結果を渡すようにしますか?

4

2 に答える 2

3

関数がブラック ボックスの場合、単一の反復を使用してこれを行う唯一の方法は、Seq.cache関数 (シーケンスを 1 回評価し、結果をメモリに格納する) を使用するか、シーケンスを他のメモリ内表現に変換することです。

関数がseq<T>引数として受け取る場合、それが一度だけ評価されるという保証さえありません。標準偏差の通常の実装では、最初に平均を計算し、次にシーケンスを再度反復してエラーの二乗を計算します。

1 回のパスで標準偏差を計算できるかどうかはわかりません。しかし、関数を で表現すればそれが可能ですfold。たとえば、2 つのパスを使用して最大値と平均値を計算すると、次のようになります。

let maxv = Seq.fold max Int32.MinValue input
let minv = Seq.fold min Int32.MaxValue input

次のような単一のパスを使用してそれを行うことができます。

Seq.fold (fun (s1, s2) v -> 
  (max s1 v, min s2 v)) (Int32.MinValue, Int32.MaxValue) input

ラムダ関数は少し見にくいですが、コンビネータを定義して 2 つの関数を構成できます。

let par f g (i, j) v = (f i v, g j v)
Seq.fold (par max min) (Int32.MinValue, Int32.MaxValue) input

このアプローチは、 を使用して定義できる関数に対して機能しますfold。つまり、初期値 (Int32.MinValue最初の例) と、次の値を取得したときに初期 (前の) 状態を更新するために使用される関数 (そして、おそらく結果の後処理)。一般に、このスタイルでシングルパス関数を書き直すことができるはずですが、これが標準偏差でできるかどうかはわかりません。それは間違いなく平均的に行うことができます:

let (count, sum) = Seq.fold (fun (count, sum) v -> 
  (count + 1.0, sum + v)) (0.0, 0.0) input
let mean = sum / count
于 2012-04-16T00:11:10.690 に答える
2

ここで話しているのは、次のシグネチャを持つ関数です。

(seq<'a> -> 'b) * (seq<'a> -> 'c) -> seq<'a> -> ('b * 'c)

それが関数の署名である場合、シーケンスの単一の反復を使用して上記を達成する、私が考えることができる簡単な方法はありません。まあ、それよりも効率的な方法はありません:

let magicFunc (f1:seq<'a>->'b, f2:seq<'a>->'c) (s:seq<'a>) = 
    let cached = s |> Seq.cache
    (f1 cached, f2 cached)

これにより、シーケンス自体の 1 回の反復が保証されますが (副作用があるか、遅い可能性があります)、本質的に結果をキャッシュすることによってこれを行います。キャッシュは、もう一度繰り返されます。それは何か問題がありますか?何を達成しようとしていますか?

于 2012-04-15T23:55:06.893 に答える