Nếu bạn có thể liệt kê miền của hàm và có thể so sánh các phần tử của phạm vi cho bằng nhau, bạn có thể - một cách khá đơn giản. Bằng cách liệt kê, tôi có nghĩa là có một danh sách tất cả các yếu tố có sẵn. Tôi sẽ gắn bó với Haskell, vì tôi không biết Ocaml (hoặc thậm chí làm thế nào để viết hoa nó đúng cách ;-)
Những gì bạn muốn làm là chạy qua các phần tử của miền và xem liệu chúng có bằng với phần tử của phạm vi mà bạn đang cố gắng đảo ngược hay không và lấy phần tử đầu tiên hoạt động:
inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]
Vì bạn đã nói rằng đó f
là một phản ứng từ chối, nên nhất định phải có một và chỉ một phần tử như vậy. Tất nhiên, mẹo là đảm bảo rằng việc liệt kê miền của bạn thực sự đạt đến tất cả các phần tử trong một thời gian hữu hạn . Nếu bạn đang cố gắng đảo ngược một từ chối Integer
sang Integer
, việc sử dụng [0,1 ..] ++ [-1,-2 ..]
sẽ không hoạt động vì bạn sẽ không bao giờ chuyển đến các số âm. Nói một cách cụ thể, inv ([0,1 ..] ++ [-1,-2 ..]) (+1) (-3)
sẽ không bao giờ mang lại giá trị.
Tuy nhiên, 0 : concatMap (\x -> [x,-x]) [1..]
sẽ hoạt động, vì điều này chạy qua các số nguyên theo thứ tự sau [0,1,-1,2,-2,3,-3, and so on]
. Quả thực inv (0 : concatMap (\x -> [x,-x]) [1..]) (+1) (-3)
nhanh chóng trở lại -4
!
Các Control.Monad.Omega gói có thể giúp bạn chạy qua danh sách các bộ etcetera trong một cách tốt; Tôi chắc rằng có nhiều gói như vậy nữa - nhưng tôi không biết chúng.
Tất nhiên, cách làm này khá thấp và thô bạo, chưa kể là xấu xí và không hiệu quả! Vì vậy, tôi sẽ kết thúc bằng một vài nhận xét về phần cuối cùng của câu hỏi của bạn, về cách 'viết' các tiểu sử. Loại hệ thống của Haskell không phải để chứng minh rằng một chức năng là một phản ứng - bạn thực sự muốn một cái gì đó giống như Agda cho điều đó - nhưng nó sẵn sàng tin tưởng bạn.
(Cảnh báo: sau mã chưa được kiểm tra)
Vì vậy, bạn có thể xác định một kiểu dữ liệu của Bijection
s giữa các kiểu a
và b
:
data Bi a b = Bi {
apply :: a -> b,
invert :: b -> a
}
cùng với bao nhiêu hằng số (nơi bạn có thể nói 'Tôi biết chúng là những đường nhị phân!') như bạn muốn, chẳng hạn như:
notBi :: Bi Bool Bool
notBi = Bi not not
add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)
và một số tổ hợp thông minh, chẳng hạn như:
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)
Tôi nghĩ bạn có thể làm invert (mapBi add1Bi) [1,5,6]
và nhận được [0,4,5]
. Nếu bạn chọn các tổ hợp của mình một cách thông minh, tôi nghĩ rằng số lần bạn phải viết một Bi
hằng số bằng tay có thể khá hạn chế.
Rốt cuộc, nếu bạn biết một hàm là một phép phân tích, hy vọng bạn sẽ có một bản phác thảo bằng chứng về thực tế đó trong đầu, mà phép đẳng cấu Curry-Howard sẽ có thể biến thành một chương trình :-)
f x = 1
, nghịch đảo của 1 là một tập hợp các số nguyên và nghịch đảo của bất kỳ thứ gì khác là một tập rỗng. Bất kể một số câu trả lời nói gì, chức năng không mang tính phân tích không phải là vấn đề lớn nhất.