Подтвердить что ты не робот

Применение нескольких функций в одном и том же стиле без указания точки в Haskell

Мне было скучно в один прекрасный день и я хотел заниматься своим мозгом, поэтому решил сделать 99 Haskell Problems, но ограничился тем, что делал их в стиле без очков. Проблема, которая, как представляется, очень часто возникает, когда я делаю что-то в бесшумном стиле, такова: как вы применяете несколько функций к одному и тому же значению, сохраняя каждый результат как независимый объект? Использование заостренных обозначений:

foobar x = [id x, reverse x]

И то, что я придумал до сих пор в беспредметной нотации:

foobar' = `map` [id, reverse] ($ x)

Похоже, что я не получаю это x от конца.

4b9b3361

Ответ 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)