Se puoi enumerare il dominio della funzione e puoi confrontare gli elementi dell'intervallo per l'uguaglianza, puoi farlo in un modo piuttosto semplice. Con enumerare intendo avere un elenco di tutti gli elementi disponibili. Mi atterrò a Haskell, dal momento che non conosco Ocaml (o anche come capitalizzarlo correttamente ;-)
Quello che vuoi fare è scorrere gli elementi del dominio e vedere se sono uguali all'elemento dell'intervallo che stai cercando di invertire, e prendere il primo che funziona:
inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]
Dato che hai affermato che f
è una biiezione, deve esserci uno e solo uno di questi elementi. Il trucco, ovviamente, è garantire che la tua enumerazione del dominio raggiunga effettivamente tutti gli elementi in un tempo finito . Se stai cercando di invertire una biiezione da Integer
a Integer
, l'utilizzo [0,1 ..] ++ [-1,-2 ..]
non funzionerà poiché non arriverai mai ai numeri negativi. In concreto, inv ([0,1 ..] ++ [-1,-2 ..]) (+1) (-3)
non produrrà mai un valore.
Tuttavia, 0 : concatMap (\x -> [x,-x]) [1..]
funzionerà, poiché questo scorre attraverso gli interi nel seguente ordine [0,1,-1,2,-2,3,-3, and so on]
. Anzi inv (0 : concatMap (\x -> [x,-x]) [1..]) (+1) (-3)
prontamente ritorna -4
!
Il pacchetto Control.Monad.Omega può aiutarti a scorrere gli elenchi di tuple e così via in un buon modo; Sono sicuro che ci sono più pacchetti del genere, ma non li conosco.
Naturalmente, questo approccio è piuttosto semplice e bruta, per non parlare di brutto e inefficiente! Quindi concludo con alcune osservazioni sull'ultima parte della tua domanda, su come "scrivere" le biiezioni. Il sistema di tipi di Haskell non è in grado di dimostrare che una funzione è una biiezione - vuoi davvero qualcosa come Agda per questo - ma è disposto a fidarsi di te.
(Attenzione: segue codice non testato)
Quindi puoi definire un tipo di dati di Bijection
s tra i tipi a
e b
:
data Bi a b = Bi {
apply :: a -> b,
invert :: b -> a
}
insieme a tutte le costanti (dove puoi dire " So che sono biiezioni!") quante ne desideri, ad esempio:
notBi :: Bi Bool Bool
notBi = Bi not not
add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)
e un paio di combinatori intelligenti, come:
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)
Penso che potresti fare invert (mapBi add1Bi) [1,5,6]
e ottenere [0,4,5]
. Se scegli i tuoi combinatori in modo intelligente, penso che il numero di volte che dovrai scrivere una Bi
costante a mano potrebbe essere piuttosto limitato.
Dopotutto, se sai che una funzione è una biiezione, si spera che tu abbia un abbozzo di prova di quel fatto nella tua testa, che l'isomorfismo di Curry-Howard dovrebbe essere in grado di trasformare in un programma :-)
f x = 1
, l'inverso di 1 è un insieme di numeri interi e l'inverso di qualsiasi altra cosa è un insieme vuoto. Indipendentemente da ciò che dicono alcune risposte, la funzione non essere biiettiva non è il problema più grande.