Я знаком со стандартом zipWith
функции, которые воздействуют на соответствующие элементы двух последовательностей, но на функциональном языке (или языке с некоторыми функциональными возможностями), что самый сжатый путь состоит в том, чтобы условно выбрать пар элементов, которые будут заархивированы, на основе третьей последовательности?
Это любопытство возникло при царапании нескольких вещей в Excel.
С числами в A1:A10, B1:B10, C1:C10, D1, E1 и F1, я использую формулу как это:
{=AVERAGE(IF((D1<=(A1:A10))*((A1:A10)<=E1),B1:B10/C1:C10))}
Каждая половина умножения в операторе IF произведет массив булевых значений, которые затем умножаются (AND'ed) вместе. То управление булевскими переменными, которое из этих десяти частных будет в конечном счете усреднено, таким образом, это будет, как будто оценивались десять отдельных операторов IF.
Если, например, только второе и треть 10 значений в A1:A10 удовлетворяют условия (оба> =D1 и <=E1), то формула заканчивает тем, что оценила таким образом:
AVERAGE(FALSE,B2/C2,B3/C3,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE)
СРЕДНЯЯ функция, оказывается, игнорирует булевскую переменную и текстовые значения, таким образом, мы просто получаем среднее число вторых и третьих частных.
Это может быть сделано как кратко с Haskell? Erlang? LINQ или F#? Python? и т.д.
ОБРАТИТЕ ВНИМАНИЕ, что для этого конкретного примера, формула, данная выше, не совершенно корректна - это было сокращено, чтобы убедительно изложить свою точку зрения. Когда ни один из этих десяти элементов в A1:A10 не удовлетворит условия, затем десять ЛОЖНЫХ значений будут переданы СРЕДНЕМУ ЧИСЛУ, которое неправильно оценит к 0.
Формула должна быть записана этот путь:
{=AVERAGE(IF(NOT(OR((D1<=(A1:A10))*((A1:A10)<=E1))),NA(),
IF((D1<=(A1:A10))*((A1:A10)<=E1),B1:B10/C1:C10)))}
Где NA()
производит ошибку, указывая, что среднее число не определено.
Спасибо за ответы. Я понял, что мой первый вопрос был довольно тривиален, с точки зрения применения функции на парах элементов из вторых и третьих списков, когда соответствующий элемент из первого списка соответствует некоторым конкретным критериям. Я принял ответ Norman Ramsey для этого.
Однако то, куда я перешел к следующему, задавалось вопросом, могла ли функция быть применена к кортежу, представляющему соответствующие элементы от произвольного числа списков - следовательно мой вопрос Lebertram о пределах zipWithN
.
Информация Apocalisp о применимых функторах привела меня к информации о распаковке Python списков аргументов - применение функции к произвольному числу аргументов.
Для определенного примера я дал выше, насчитав частных элементов списков (где nums
список списков), похоже, что Python может сделать это как это:
from operator import div
def avg(a): return sum(a,0.0)/len(a)
avg([reduce(div,t[1:]) for t in zip(*nums) if d<=t[0] and t[0]<=e])
В более общем плане, с функцией f
и предикат p
(наряду с avg
) это становится:
avg([f(t[1:]) for t in zip(*nums) if p(t[0])])
Как условно выделить элементы в zip?
Сначала запечатать, потом выделить.
В данном случае я делаю выборку с помощью catMaybes
, что часто бывает полезно в данной ситуации. Добиться проверки на типизацию было большой проблемой (нужно поместить fromIntegral
в точно нужное место), но вот код, который я бы написал, полагаясь на оптимизатор, как обычно:
average as bs cs d1 e1 = avg $ catMaybes $ zipWith3 cdiv as bs cs
where cdiv a b c = if a >= d1 && a <= e1 then Just (b/c) else Nothing
avg l = sum l / fromIntegral (length l)
Функция cdiv
означает "условное деление".
Чтобы получить catMaybes
, нужно импортировать Data.Maybe
.
Этот код проверяет тип, но я его не запускал.
То, что вы ищете, это Аппликативные векторы. В частности, "молниеносный" аппликативный вектор из статьи по ссылке.
В нотации Хаскеля назовем вашу функцию f
. Тогда в прикладном программировании это будет выглядеть так:
f d e as bs cs = if' <$> ((&&) <$> (d <=) <*> (e >=))
<$> as <*> ((/) <$> bs <*> cs) <*> (repeat 0)
where if' x y z = if x then y else z
(<*>) = zipWith ($)
Результатом f
будет список. Просто возьмите среднее значение. Немного обобщим:
f g p as bs cs = if' <$> p <$> as <*> (((Some .) . g) <$> bs <*> cs)
<*> (repeat None)
Здесь p - это предикат, поэтому вы бы вызвали его с помощью:
average $ fromMaybe 0 <$> f (/) ((&&) <$> (d <=) <*> (e >=)) as bs cs
... учитывая то же определение <*>
, что и выше.
Примечание: я не проверял этот код, поэтому могут быть пропущены круглые скобки и т.п., но идея понятна.
Haskell:
average . map fromJust . filter isJust $ zipWith3 (\a b c -> if a >= d1 && a <= e1 then Just b/c else Nothing) as bs cs
where average xs = let (sum,n) = foldl' (\(s,m) x -> (s+x,m+1)) (0,0) xs in sum / (fromIntegral n)