Apply several aggregate functions with one enumeration

Refresh

November 2018

Views

166 time

3

Let's assume I have a series of functions that work on a sequence, and I want to use them together in the following fashion:

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

The code above is going to enumerate the sequence twice. I am interested in a function that will give the same result but enumerate the sequence only once. This function will be something like this:

magicFunction (mean, standardDeviation) data

where the input is a tuple of functions and a sequence and the ouput is the same with the function above.

Is this possible if the functions mean and stadardDeviation are black boxes and I cannot change their implementation?

If I wrote mean and standardDeviation myself, is there a way to make them work together? Maybe somehow making them keep yielding the input to the next function and hand over the result when they are done?

2 answers

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)

Это обеспечивает единственную итерацию самой последовательности (возможно, есть побочные эффекты, или это медленно), но делает это по существу кэширование результатов. Кэш еще итерация в другой раз. Что случилось с этим? Что вы пытаетесь достичь?

3

Единственный способ сделать это , используя только одну итерацию , когда функции являются черные ящики будет использовать Seq.cacheфункцию (которая оценивает последовательность раз и сохраняет результаты в памяти) или для преобразования последовательности в другое представление в памяти.

Когда функция принимает в seq<T>качестве аргумента, вы даже не иметь гарантию , что он будет оценивать его только один раз - и обычные реализации стандартного отклонения сначала вычислить среднее , а затем итерации по последовательности снова вычислить квадраты ошибок.

Я не уверен , если вы можете рассчитать стандартное отклонение только с одного прохода. Тем не менее, можно сделать, если выражены функции с использованием fold. Например, вычисление максимального и среднего с использованием двух проходов выглядит следующим образом :

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

Лямбда-функция немного некрасиво, но вы можете определить комбинатор составлять две функции:

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
Смотреть фильмы онлайн бабушка легкого поведения 2 фильм бесплатно