На функциональном языке, как условно выбрать элементы, которые будут использоваться в zip - или функция zipWith-стиля?

Я знаком со стандартом 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])])
5
задан Dave 22 February 2010 в 21:33
поделиться

3 ответа

Как условно выделить элементы в 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.

Этот код проверяет тип, но я его не запускал.

3
ответ дан 14 December 2019 в 08:49
поделиться

То, что вы ищете, это Аппликативные векторы. В частности, "молниеносный" аппликативный вектор из статьи по ссылке.

В нотации Хаскеля назовем вашу функцию 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

... учитывая то же определение <*>, что и выше.

Примечание: я не проверял этот код, поэтому могут быть пропущены круглые скобки и т.п., но идея понятна.

3
ответ дан 14 December 2019 в 08:49
поделиться

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)
1
ответ дан 14 December 2019 в 08:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: