Есть ли лучший способ пересечения карт объединения?

Недавно я несколько раз сталкивался с этим шаблоном. В основном он обрабатывает все ключи, as, as и в bs и не касается других клавиш.

foo :: (a -> b -> a) -> Map a -> Map b -> Map a
foo f as bs = (Map.intersectionWith f as bs) 'Map.union' (Map.difference as bs)

Из-за характера union это можно сократить до:

bar :: (a -> b -> a) -> Map a -> Map b -> Map a
bar f as bs = Map.intersectionWith f as bs 'Map.union' as

Но все - таки это потребует три обходов (два более, as один над bs). Моя интуиция заключается в том, что должны быть способы решить эту проблему только двумя обходами.

1 ответ

На самом деле это прямо там: Map.differenceWith правильно, что я хочу. Это немного неинтуитивно, но в основном он принимает фактическую разницу as и bs и дает возможность включать значения из пересечения в результирующий набор.

Поэтому это

foo :: (a -> b -> a) -> Map a -> Map b -> Map a
foo f as bs = (Map.intersectionWith f as bs) 'Map.union' (Map.difference as bs)

можно переписать как

baz :: (a -> b -> a) -> Map a -> Map b -> Map a
baz f as bs = Map.differenceWith (\a b -> Just (f a b)) as bs

Простой... извините за то, что тратил свое время :)

licensed under cc by-sa 3.0 with attribution.