Функция подъема `a → b → c` в`[a] → [b] → [[c]]`
Я хотел бы иметь функцию
foo :: (a → b → c) → [a] → [b] → [[c]]
это берет функцию f :: a → b → c
и два списка xs
а также ys
и возвращает сетку (то есть список списков), содержащий значения f
применяется к каждой комбинации значений из xs
а также ys
,
Пример: foo [1..3] [4..6]
должен вернуться
[[f 1 4,f 1 5,f 1 6],
[f 2 4,f 2 5,f 2 6],
[f 3 4,f 3 5,f 3 6]]
Мой текущий подход
foo = traverse . flip . traverse . flip
Это работает, но мне интересно, есть ли какой-то другой подход или предопределенный комбинатор, с помощью которого это можно сделать более красиво (или, возможно, даже составно, чтобы его можно было легко распространить на троичные или n-арные функции)
Например: если бы я не хотел таблицу результатов, а просто список результатов, я мог бы написать f <$> xs <*> ys
, который является кратким, использует предопределенные комбинаторы и обобщает n-арные функции очевидным образом. Есть ли такой же краткий способ написания моего комбинатора?
2 ответа
Это работа для понимания списка!
foo f xs ys = [ [ f x y | y <- ys ] | x <- xs]
Прецедент:
foo (\x y -> show x ++ " " ++ show y) [1..3] [4..6]
выходы:
[["1 4","1 5","1 6"],["2 4","2 5","2 6"],["3 4","3 5","3 6"]
Кроме того, это решение не обобщается на произвольные обходные пути?
Это делает (и даже больше): оба понимания списка могут быть заменены fmap
, уступая
foo :: (Functor f, Functor g) => (a -> b -> c) -> f a -> g b -> f (g c)
foo f xs ys = fmap (\x -> fmap (\y -> f x y) ys) xs
Теперь несколько упрощений:
\y -> f x y === f x
fmap (f x) ys === flip fmap ys (f x) === flip fmap ys . f $ x
\x -> flip fmap ys . f $ x === flip fmap ys . f
Так
foo f xs ys = fmap (flip fmap ys . f) xs