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

Я знаком со стандартными 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). Эти булевы значения определяют, какой из десяти факторов будет в конечном итоге усреднен, так что, как будто оцениваются десять отдельных операторов IF.

Если, например, только второе и третье из 10 значений в A1: A10 удовлетворяют условиям (как> = D1, так и <= E1), то формула в итоге оценивается следующим образом: </p>

AVERAGE(FALSE,B2/C2,B3/C3,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE)

Функция AVERAGE игнорирует логические и текстовые значения, поэтому мы просто получаем среднее значение второго и третьего коэффициентов.

Может ли это быть сделано кратко с помощью Haskell? Erlang? LINQ или F #? Python? и т.д ..

ОБРАТИТЕ ВНИМАНИЕ, что для этого конкретного примера приведенная выше формула не совсем верна - она ​​была сокращена для объяснения сути. Если ни один из десяти элементов в A1: A10 не удовлетворяет условиям, тогда десять значений FALSE будут переданы в AVERAGE, который неверно оценивается как 0.
Формула должна быть написана так:

{=AVERAGE(IF(NOT(OR((D1<=(A1:A10))*((A1:A10)<=E1))),NA(),
             IF((D1<=(A1:A10))*((A1:A10)<=E1),B1:B10/C1:C10)))}

Где NA() выдает ошибку, указывающую, что среднее значение не определено.

Обновление:

Спасибо за ответы. Я понял, что мой первый вопрос был довольно тривиальным с точки зрения применения функции к парам элементов из второго и третьего списков, когда соответствующий элемент из первого списка удовлетворяет некоторым конкретным критериям. Я принял ответ Нормана Рэмси за это.

Однако, когда я перешел к следующему, мне было интересно, можно ли применить эту функцию к кортежу, представляющему соответствующие элементы из произвольного числа списков - отсюда мой вопрос к Лебертраму о пределах 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])])

Ответы [ 3 ]

3 голосов
/ 21 февраля 2010

То, что вы ищете, это Аппликативные функторы . В частности, аппликатив «молния» из связанной бумаги.

В нотации Haskell давайте назовем вашу функцию 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 голосов
/ 22 февраля 2010

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

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

1 голос
/ 21 февраля 2010

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)
...