Jeśli potrafisz wyliczyć dziedzinę funkcji i możesz porównać elementy zakresu pod kątem równości, możesz - w dość prosty sposób. Przez wyliczenie mam na myśli posiadanie listy wszystkich dostępnych elementów. Zostanę przy Haskellu, bo nie znam Ocamla (ani nawet jak go odpowiednio skapitalizować ;-)
To, co chcesz zrobić, to przejrzeć elementy domeny i sprawdzić, czy są one równe elementowi zakresu, który próbujesz odwrócić, i wybrać pierwszy, który działa:
inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]
Ponieważ stwierdziłeś, że f
jest to bijekcja, musi istnieć jeden i tylko jeden taki element. Rzecz w tym, by upewnić się, że wyliczenie domeny faktycznie dotrze do wszystkich elementów w określonym czasie . Jeśli próbujesz odwrócić bijekcję od Integer
do Integer
, użycie [0,1 ..] ++ [-1,-2 ..]
nie zadziała, ponieważ nigdy nie dojdziesz do liczb ujemnych. A konkretnie, inv ([0,1 ..] ++ [-1,-2 ..]) (+1) (-3)
nigdy nie przyniesie wartości.
Jednak 0 : concatMap (\x -> [x,-x]) [1..]
zadziała, ponieważ przebiega przez liczby całkowite w następującej kolejności [0,1,-1,2,-2,3,-3, and so on]
. Rzeczywiście, inv (0 : concatMap (\x -> [x,-x]) [1..]) (+1) (-3)
szybko wraca -4
!
Control.Monad.Omega pakiet może pomóc uruchomić poprzez list krotki etcetera w dobrym tego słowa znaczeniu; Jestem pewien, że takich pakietów jest więcej - ale ich nie znam.
Oczywiście to podejście jest raczej powściągliwe i brutalne, nie wspominając o brzydkim i nieefektywnym! Więc zakończę kilkoma uwagami na temat ostatniej części twojego pytania, jak „pisać” bijezje. System typów Haskella nie jest w stanie udowodnić, że funkcja jest bijection - naprawdę chcesz do tego czegoś takiego jak Agda - ale chce ci zaufać.
(Ostrzeżenie: następuje nieprzetestowany kod)
Czy możesz więc zdefiniować typ danych Bijection
s między typami a
a b
:
data Bi a b = Bi {
apply :: a -> b,
invert :: b -> a
}
wraz z tyloma stałymi (gdzie możesz powiedzieć „ Wiem, że to bijekty!”), ile chcesz, na przykład:
notBi :: Bi Bool Bool
notBi = Bi not not
add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)
i kilka inteligentnych kombinatorów, takich jak:
idBi :: Bi a a
idBi = Bi id id
invertBi :: Bi a b -> Bi b a
invertBi (Bi a i) = (Bi i a)
composeBi :: Bi a b -> Bi b c -> Bi a c
composeBi (Bi a1 i1) (Bi a2 i2) = Bi (a2 . a1) (i1 . i2)
mapBi :: Bi a b -> Bi [a] [b]
mapBi (Bi a i) = Bi (map a) (map i)
bruteForceBi :: Eq b => [a] -> (a -> b) -> Bi a b
bruteForceBi domain f = Bi f (inv domain f)
Myślę, że mógłbyś wtedy zrobić invert (mapBi add1Bi) [1,5,6]
i dostać [0,4,5]
. Jeśli mądrze wybierzesz kombinatory, myślę, że ile razy będziesz musiał napisaćBi
ręcznie stałą, może być dość ograniczona.
W końcu, jeśli wiesz, że funkcja jest bijection, miejmy nadzieję, że będziesz miał w głowie szkic dowodowy tego faktu, który izomorfizm Curry-Howarda powinien być w stanie przekształcić w program :-)
f x = 1
odwrotności 1 jest zbiorem liczb całkowitych, a odwrotnością czegokolwiek innego jest zbiorem pustym. Niezależnie od tego, co mówią niektóre odpowiedzi, funkcja, która nie jest bijektywna, nie jest największym problemem.