Применение нескольких функций к одному и тому же стилю без точек значения в 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)