Применение нескольких функций в одном и том же стиле без указания точки в Haskell
Мне было скучно в один прекрасный день и я хотел заниматься своим мозгом, поэтому решил сделать 99 Haskell Problems, но ограничился тем, что делал их в стиле без очков. Проблема, которая, как представляется, очень часто возникает, когда я делаю что-то в бесшумном стиле, такова: как вы применяете несколько функций к одному и тому же значению, сохраняя каждый результат как независимый объект? Использование заостренных обозначений:
foobar x = [id x, reverse x]
И то, что я придумал до сих пор в беспредметной нотации:
foobar' = `map` [id, reverse] ($ x)
Похоже, что я не получаю это x
от конца.
Ответы
Ответ 1
Другие уже опубликовали, как вы можете это сделать, используя монаду Reader
, но это не единственный способ. Оказывается, ваша вторая функция довольно близка. Я думаю, вы хотели опубликовать
foobar' x = (`map` [id, reverse]) ($ x)
Так как x
уже находится в крайнем правом положении, вы почти там. Сначала преобразуйте раздел ($ x)
в функцию, потому что с ним немного легче работать:
-- by the definition of a right operator section
foobar'2 x = (`map` [id, reverse]) (\y -> ($) y x)
Затем удалите x
из тела лямбда, введя новую переменную в область видимости и применив ее к x
-- lambda abstraction I think...
foobar'2 x = (`map` [id, reverse]) $ (\z y -> ($) y z) x
Перепишите это приложение как составную функцию, а затем вы можете уменьшить это:
-- by definition of '.'
foobar'3 x = (`map` [id, reverse]) . (\z y -> ($) y z) $ x
-- eta reduction
foobar'4 = (`map` [id, reverse]) . (\z y -> ($) y z)
Наконец, обратите внимание, что мы можем заменить лямбда функцией
-- by definition of `flip`
foobar'5 = (`map` [id,reverse]) . flip ($)
и у вас есть точка-свободная форма.
Ответ 2
Используйте sequence:
> let foobar' = sequence [id, reverse]
> foobar' "abcde"
["abcde","edcba"]
Ответ 3
Вас будет интересовать экземпляр Applicative
монады читателя:
instance Applicative (e ->)
Используя его, вы можете легко распределить аргумент:
liftA2 (+) sin cos 3
Здесь sin
и cos
- это функции, которые оба получают значение 3. Затем индивидуальные результаты объединяются с использованием (+)
. Вы можете объединить это с экземпляром Category
(->)
, но специализированные версии cource (.)
и id
уже определены в Prelude
.
Фон: экземпляр Applicative
для (e ->)
действительно представляет исчисление SKI, где (<*>)
является S-комбинатором, а pure
является комбинатором K. S точно используется для распределения аргумента для двух функций:
S f g x = f x (g x)
Он принимает приложение функции (f g) и заставляет оба зависимы от значения x ((f x) (g x)).
Ответ 4
Есть несколько основных идиоматических комбинаторов, которые появляются многократно и переопределяются различными высшими концепциями и библиотеками, но которые по сути очень просты. Имена могут различаться, а некоторые могут быть реализованы с точки зрения других:
fork (f,g) x = (f x, g x) -- == (f &&& g)
prod (f,g) x = (f $ fst x, g $ snd x) -- == (f *** g)
pmap f (x,y) = (f x, f y) -- == (f *** f)
dup x = (x,x)
и т.д.. Конечно, uncurry f (x,y) == f x y
тоже много используется с ними.
&&&
и ***
определены в Control.Arrow
, а также first
и second
. Тогда prod (f,id) == first f
, prod(id,g) == second g
и т.д. И т.д.
Итак, ваш foobar
становится
foobar = (\(a,b)->[a,b]) . fork (id,reverse)
= (\(a,b)->[a,b]) . (id &&& reverse)
= (\(a,b)->[a,b]) . (id *** reverse) . dup
= join $ curry ( (\(a,b)->[a,b]) . second reverse)
Для последнего нужно также импортировать Control.Monad
и Control.Monad.Instances
. См. Также этот вопрос.
позднее редактирование: также, используя Control.Applicative
, как намечено в ответ ertes,
= (:) <*> ((:[]) . reverse)