Применение нескольких функций к одному и тому же стилю без точек значения в Haskell
однажды мне стало скучно и захотелось потренировать свой мозг, поэтому я решил сделать 99 Haskell Проблемы но ограничился тем, что делал их в стиле без точек. Проблема, которая, кажется, возникает много, когда я делаю вещи в стиле без точек: как вы применяете несколько функций к одному и тому же значению, сохраняя каждый результат как независимую сущность? Использование заостренных обозначений:
foobar x = [id x, reverse x]
и то, что я придумал до сих пор в point-free обозначение:
foobar' = `map` [id, reverse] ($ x)
Я не могу этого понять x
С конца там.
4 ответов
другое уже написал, как вы можете сделать это с помощью 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
перепишите это приложение как функциональную композицию, а затем вы можете уменьшить eta:
-- 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 ($)
и у вас есть точка-свободная форма.
вы будете заинтересованы в Applicative
экземпляр читательской монады:
instance Applicative (e ->)
используя его, вы можете легко распространять аргумент:
liftA2 (+) sin cos 3
здесь sin
и cos
являются функциями, которые оба получают значение 3. Индивидуальные результаты затем объединяются с помощью (+)
. Вы можете дополнительно объединить это с Category
экземпляр (->)
, но конечно специализированные версии (.)
и id
уже определены в Prelude
.
Справочная Информация:Applicative
экземпляр (e ->)
действительно представляет Лыжное исчисление, где (<*>)
- это S Combinator и pure
это K комбинатора. S точно используется для распределения аргумента на две функции:
S f g x = f x (g x)
он принимает приложение функции (f g) и делает оба зависимыми от значения x ((f x) (g x)).
использовать последовательность:
> let foobar' = sequence [id, reverse]
> foobar' "abcde"
["abcde","edcba"]
есть несколько основных идиоматических комбинаторов, которые появляются неоднократно и переосмысливаются с различными более высокими понятиями и библиотеками, но которые по существу очень просты. Имена могут отличаться, и некоторые из них реализуемы с точки зрения других:
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)
etc. Конечно!--3--> часто используется с ними.
&&&
и ***
определена в Control.Arrow
, а также first
и second
. Тогда prod (f,id) == first f
, prod(id,g) == second g
etc. так далее.
так что ваши 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
как намекнул в ответ эртес,
= (:) <*> ((:[]) . reverse)