Итак, я играю с функцией сопряжения Кантора и пытаюсь следовать формулам Википедии как можно ближе.
type N = Int
toCantor :: (N, N) -> N
fromCantor :: N -> (N, N)
toCantor (x, y) = (x + y) * (x + y + 1) `div` 2 + y
type N
, чтобы я моглегко изменить на Integer
позже (некоторые из промежуточных кальков станут большими). - неиспечённая форма, частично следуя wp, частично так
(fromCantor . toCantor) === id
и (toCantor . fromCantor) === id
.
Снова после wp:
fromCantor z = (x, y) where
x = w - y
y = z - t
t = (w * w + w) `div` 2
w = floor $ (sqrt (fromIntegral (z * 8 + 1)) - 1.0) / 2.0
Это работает и все, кроме того, что формула для w
ужасна!
- Нужны все эти парены, потому что у меня есть формулавложенный в вызов функции и свободная привязка
(-)
вложенная в жесткую привязку (/)
. - (и оба эти оператора не являются коммутативными, поэтому я должен быть осторожен.)
Q 1. Есть ли способ сделать эту формулу более симпатичной / точечной?
Я вижу, что формула начинается с z
и строится снаружи.Таким образом, я могу выполнить расчет:
(.|) :: a -> (a -> b) -> b -- pipelining
infixl 0 .|
x .| f = f x -- aka flip ($)
wP :: N -> N -- w with Pipelining
wP z = z
.| (* 8)
.| (+ 1)
.| fromIntegral
.| sqrt
.| subtract 1.0
.| (/ 2.0)
.| floor
Является ли этот стиль предшествующим уровнем техники?Является ли (.|)
хорошим способом для написания этой операции - я думаю, что видел ее как оператор Lens (?)
Q 2. Я (намеренно) утверждал, чтов стиле псевдомонады.Может ли это быть блок do
?
- Сначала мне нужна монада.Я мог бы использовать
Maybe
или (Either e)
- что было бы хорошо, потому что некоторые из этих функций являются частичными, и я должен использовать безопасную версию. - Тогда вместо
z
I 'd поставить return z
. - Но переплет идет не так, как нужно.Вместо
Monad m => m a -> (a -> m b) -> m b
я хочу Monad m => m a -> (a -> b) -> m b
.Это похоже на fmap
, но перевернуто. - Я мог бы применить некоторый вид поднятия к функциям / операторам, но это затем затушевало бы арифметику с монадным слесарным делом.
- Повторный синтаксис?