ในภาษาที่ใช้งานได้จริงมีอัลกอริทึมในการรับฟังก์ชันผกผันหรือไม่?


100

ในภาษาที่ใช้งานได้อย่างแท้จริงเช่น Haskell มีอัลกอริทึมในการรับค่าผกผันของฟังก์ชัน (แก้ไข) เมื่อเป็น bijective หรือไม่? และมีวิธีเฉพาะเจาะจงในการตั้งโปรแกรมฟังก์ชันของคุณหรือไม่?


5
ในทางคณิตศาสตร์ไม่ผิดที่จะกล่าวว่าในกรณีของf x = 1การผกผันของ 1 คือเซตของจำนวนเต็มและสิ่งที่ผกผันของสิ่งอื่นเป็นเซตว่าง ไม่ว่าคำตอบบางคำจะพูดว่าอย่างไรฟังก์ชันที่ไม่เป็นอคติไม่ใช่ปัญหาใหญ่ที่สุด
Karolis Juodelė

2
คำตอบที่ถูกต้องคือใช่ แต่ไม่มีประสิทธิภาพ ให้ f: A -> B และ A จำกัด จากนั้นให้ b € B คุณ "เท่านั้น" ต้องตรวจสอบ f (A) ทั้งหมดเพื่อค้นหา€ A ทั้งหมดที่ f (a) = b ในคอมพิวเตอร์ควอนตัมอาจมีความซับซ้อน O (size (a)) แน่นอนคุณมองหาอัลกอริทึมที่ใช้ได้จริง ไม่ใช่ (มี O (2 ^ size (a))) แต่มีอยู่ ...
josejuan

QuickCheck กำลังทำอย่างนั้น (พวกเขามองหา False ใน f: A -> Bool)
josejuan

4
@ KarolisJuodelė: ฉันไม่เห็นด้วย; นั่นไม่ใช่สิ่งที่หมายถึงผกผัน สวยมากทุกครั้งที่ผมพบคำผกผันของการfเป็นฟังก์ชั่นgดังกล่าวว่าและf . g = id g . f = idผู้สมัครของคุณไม่ได้พิมพ์ตรวจสอบในกรณีนั้น
Ben Millwood

3
@ BenMillwood คุณพูดถูก สิ่งที่ฉันพูดเรียกว่าภาพผกผันไม่ใช่ฟังก์ชันผกผัน ประเด็นของฉันคือคำตอบที่ชี้ให้เห็นว่าf x = 1ไม่มีการผกผันใช้แนวทางที่แคบมากและไม่สนใจความซับซ้อนทั้งหมดของปัญหา
Karolis Juodelė

คำตอบ:


101

ในบางกรณีใช่! มีกระดาษสวย ๆ ชื่อว่าBidirectionalization ฟรี! ซึ่งจะกล่าวถึงบางกรณี - เมื่อฟังก์ชันของคุณมีความหลากหลายเพียงพอ - ในกรณีที่เป็นไปได้จะได้รับฟังก์ชันผกผันโดยอัตโนมัติ (นอกจากนี้ยังกล่าวถึงสิ่งที่ทำให้ปัญหาหนักขึ้นเมื่อฟังก์ชั่นไม่ใช่ความหลากหลาย)

สิ่งที่คุณจะได้รับในกรณีที่ฟังก์ชันของคุณไม่สามารถกลับด้านได้คือค่าผกผัน (พร้อมอินพุตปลอม) ในกรณีอื่นคุณจะได้รับฟังก์ชันที่พยายาม "ผสาน" ค่าอินพุตเก่าและค่าเอาต์พุตใหม่


3
นี่คือบทความล่าสุดที่สำรวจความทันสมัยในการปรับเปลี่ยนทิศทางสองทิศทาง ซึ่งรวมถึงเทคนิคสามตระกูลซึ่งรวมถึง "วากยสัมพันธ์" และแนวทางที่ใช้ Combinator เช่นกัน: iai.uni-bonn.de/~jv/ssgip-bidirectional-final.pdf
sclv

และเพียงแค่พูดถึงในปี 2008 มีข้อความนี้ถึง -cafe โดยมีแฮ็กชั่วร้ายสำหรับการสลับputฟังก์ชันไปยังโครงสร้างบันทึกใด ๆ ที่ได้มาData: haskell.org/pipermail/haskell-cafe/2008-April/042193.htmlโดยใช้วิธีการคล้ายกับ ที่นำเสนอในภายหลัง (เข้มงวดมากขึ้นโดยทั่วไปมีหลักการมากขึ้น ฯลฯ ) ใน "ฟรี"
sclv

เป็นปี 2017 และแน่นอนว่าลิงก์ไปยังกระดาษนั้นใช้ไม่ได้อีกต่อไปนี่คือลิงก์ที่อัปเดตแล้ว: pdfs.semanticscholar.org/5f0d/…
Mina Gabriel

37

ไม่เป็นไปไม่ได้โดยทั่วไป

หลักฐาน: พิจารณาฟังก์ชันทางชีวภาพของประเภท

type F = [Bit] -> [Bit]

ด้วย

data Bit = B0 | B1

สมมติเรามีอินเวอร์เตอร์ดังกล่าวว่าinv :: F -> F inv f . f ≡ idสมมติว่าเราได้ทดสอบฟังก์ชันf = idนี้แล้วโดยยืนยัน

inv f (repeat B0) -> (B0 : ls)

เนื่องจากสิ่งนี้เป็นอันดับแรกB0ในเอาต์พุตต้องมาหลังจากเวลาที่ จำกัด เราจึงมีขอบเขตnบนทั้งความลึกที่invประเมินอินพุตการทดสอบของเราเพื่อให้ได้ผลลัพธ์นี้รวมถึงจำนวนครั้งที่สามารถเรียกfได้ ตอนนี้กำหนดกลุ่มของฟังก์ชัน

g j (B1 : B0 : ... (n+j times) ... B0 : ls)
   = B0 : ... (n+j times) ... B0 : B1 : ls
g j (B0 : ... (n+j times) ... B0 : B1 : ls)
   = B1 : B0 : ... (n+j times) ... B0 : ls
g j l = l

เห็นได้ชัดว่าทั้งหมด0<j≤n, g jเป็น bijection ในความเป็นจริงด้วยตนเองผกผัน ดังนั้นเราควรจะสามารถยืนยันได้

inv (g j) (replicate (n+j) B0 ++ B1 : repeat B0) -> (B1 : ls)

แต่เพื่อตอบสนองสิ่งนี้inv (g j)จะต้องมีอย่างใดอย่างหนึ่ง

  • ประเมินg j (B1 : repeat B0)ความลึกn+j > n
  • ประเมินhead $ g j lสำหรับnรายการที่แตกต่างกันอย่างน้อยที่ตรงกันreplicate (n+j) B0 ++ B1 : ls

ถึงจุดนั้นอย่างน้อยหนึ่งในg jสิ่งที่แยกไม่ออกfและเนื่องจากinv fยังไม่ได้ทำการประเมินอย่างใดอย่างหนึ่งinvจึงไม่สามารถแยกมันออกจากกันได้ - ขาดการวัดรันไทม์ด้วยตัวมันเองซึ่งเป็นไปได้เฉพาะในIO Monad.

                                                                                                                                   ⬜


19

คุณสามารถดูมันได้ในวิกิพีเดียก็เรียกว่าพลิกกลับ Computing

โดยทั่วไปคุณไม่สามารถทำได้และไม่มีภาษาที่ใช้งานได้ใดที่มีตัวเลือกนั้น ตัวอย่างเช่น:

f :: a -> Int
f _ = 1

ฟังก์ชันนี้ไม่มีการผกผัน


1
จะผิดไหมที่จะบอกว่าfมีอินเวอร์ส แต่อินเวอร์สเป็นฟังก์ชันที่ไม่ได้กำหนด
Matt Fenwick

10
@MattFenwick ตัวอย่างเช่นในภาษาเช่น Haskell ฟังก์ชันต่างๆไม่ได้เป็นสิ่งที่ไม่แน่นอน (โดยไม่ต้องเปลี่ยนประเภทและวิธีที่คุณใช้) ไม่มีฟังก์ชัน Haskell g :: Int -> aที่เป็นค่าผกผันfแม้ว่าคุณจะสามารถอธิบายการผกผันfทางคณิตศาสตร์ได้
เบ็น

2
@Matt: ค้นหา "ด้านล่าง" ในการเขียนโปรแกรมเชิงฟังก์ชันและตรรกะ ค่า "ด้านล่าง" เป็นค่าที่ "เป็นไปไม่ได้" ไม่ว่าจะเป็นเพราะขัดแย้งไม่ยุติหรือเป็นวิธีแก้ปัญหาที่ไม่สามารถตัดสินใจได้ (นี่เป็นมากกว่าความขัดแย้งเพียงอย่างเดียว - เราสามารถ "ไล่ล่า" วิธีแก้ปัญหาอย่างเป็นระบบในขณะที่เราสำรวจการออกแบบ เว้นวรรคโดยใช้ "ไม่ได้กำหนด" และ "ข้อผิดพลาด" ระหว่างการพัฒนา) A "bottom" x มีประเภทก. มัน "อาศัย" (หรือเป็น "ค่า") ทุกประเภท นี่เป็นความขัดแย้งทางตรรกะเนื่องจากประเภทเป็นประพจน์และไม่มีค่าใดที่ตรงตามทุกโจทย์ ดู Haskell-Cafe เพื่อการสนทนาที่ดี
ชื่อ

2
@ แมตต์: แทนที่จะระบุลักษณะของการผกผันที่ไม่มีอยู่ในแง่ของการไม่กำหนดปัจจัยเราต้องระบุลักษณะของมันในแง่ของก้น ค่าผกผันของ f _ = 1 คือด้านล่างเนื่องจากต้องอาศัยอยู่ในทุกประเภท (หรือเป็นด้านล่างเนื่องจาก f ไม่มีฟังก์ชันผกผันสำหรับประเภทใด ๆ ที่มีองค์ประกอบมากกว่าหนึ่งองค์ประกอบ - ฉันคิดว่าด้านที่คุณเน้น) การอยู่ล่างสามารถนำมาใช้ทั้งในทางบวกและทางลบเป็นการยืนยันเกี่ยวกับคุณค่า เราสามารถพูดถึงการผกผันของฟังก์ชันตามอำเภอใจได้อย่างสมเหตุสมผลว่าเป็นด้านล่างของ "ค่า" (แม้ว่าจะไม่ใช่ค่า "จริงๆ" ก็ตาม)
นาม

1
หลังจากเดินกลับมาที่นี่ในเวลาต่อมาฉันคิดว่าฉันได้เห็นสิ่งที่ Matt กำลังได้รับ: เรามักจะสร้างแบบจำลองความไม่เชื่อมั่นผ่านรายการและเราสามารถทำเช่นเดียวกันกับการผกผันได้ ให้ผกผันของf x = 2 * xBE f' x = [x / 2]แล้วผกผันของการมีf _ = 1 f' 1 = [minBound ..]; f' _ = []นั่นคือมีการผกผันจำนวนมากสำหรับ 1 และไม่มีค่าอื่นใด
amalloy

16

ไม่ใช่ในภาษาที่ใช้งานได้ส่วนใหญ่ แต่ในการเขียนโปรแกรมเชิงตรรกะหรือการเขียนโปรแกรมเชิงสัมพันธ์ฟังก์ชันส่วนใหญ่ที่คุณกำหนดนั้นไม่ใช่ฟังก์ชัน แต่เป็น "ความสัมพันธ์" และสามารถใช้ได้ทั้งสองทิศทาง ดูตัวอย่างเช่น prolog หรือ kanren


1
หรือดาวพุธซึ่งมีจิตวิญญาณของ Haskell เป็นจำนวนมาก - จุดดี +1
leftaround ประมาณ

11

งานเช่นนี้มักจะไม่สามารถตัดสินใจได้ คุณสามารถมีวิธีแก้ปัญหาสำหรับฟังก์ชันเฉพาะบางอย่าง แต่ไม่ใช่โดยทั่วไป

ที่นี่คุณไม่สามารถรู้ได้ด้วยซ้ำว่าฟังก์ชันใดมีการผกผัน อ้างถึงBarendregt, HP The Lambda Calculus: Syntax and Semantics นอร์ทฮอลแลนด์อัมสเตอร์ดัม (1984) :

ชุดคำศัพท์แลมบ์ดาจะไม่สำคัญหากไม่ใช่ทั้งชุดว่างหรือทั้งชุด ถ้า A และ B เป็นสองชุดที่ไม่สำคัญและไม่ปะติดปะต่อกันของคำศัพท์แลมบ์ดาที่ปิดภายใต้ความเท่าเทียมกัน (เบต้า) ดังนั้น A และ B จะแยกออกจากกันไม่ได้

ลองเอา A มาเป็นเซตของคำศัพท์แลมบ์ดาที่แสดงถึงฟังก์ชันกลับด้านและ B ส่วนที่เหลือ ทั้งสองไม่ว่างเปล่าและปิดภายใต้ความเท่าเทียมกันของเบต้า ดังนั้นจึงไม่สามารถตัดสินใจได้ว่าฟังก์ชันจะกลับด้านหรือไม่

(สิ่งนี้ใช้กับแคลคูลัสแลมบ์ดาที่ไม่ได้พิมพ์ TBH ฉันไม่รู้ว่าอาร์กิวเมนต์สามารถปรับให้เข้ากับแคลคูลัสแลมบ์ดาที่พิมพ์ได้โดยตรงหรือไม่เมื่อเรารู้ประเภทของฟังก์ชันที่เราต้องการกลับด้าน แต่ฉันค่อนข้างมั่นใจว่าจะเป็น คล้ายกัน)


11

หากคุณสามารถระบุโดเมนของฟังก์ชันและสามารถเปรียบเทียบองค์ประกอบของช่วงเพื่อความเท่าเทียมกันได้คุณสามารถทำได้อย่างตรงไปตรงมา ฉันหมายถึงการมีรายการองค์ประกอบทั้งหมดที่มีอยู่ ฉันจะยึดติดกับ Haskell เนื่องจากฉันไม่รู้จัก Ocaml (หรือแม้กระทั่งวิธีการใช้ประโยชน์อย่างถูกต้อง ;-)

สิ่งที่คุณต้องทำคือเรียกใช้องค์ประกอบของโดเมนและดูว่าพวกมันเท่ากับองค์ประกอบของช่วงที่คุณพยายามจะกลับด้านหรือไม่และเลือกองค์ประกอบแรกที่ได้ผล:

inv :: Eq b => [a] -> (a -> b) -> (b -> a)
inv domain f b = head [ a | a <- domain, f a == b ]

เนื่องจากคุณได้ระบุว่าfเป็นการคาดคะเนจึงต้องมีองค์ประกอบดังกล่าวเพียงหนึ่งเดียวเท่านั้น เคล็ดลับของหลักสูตรเพื่อให้แน่ใจว่าการแจงนับของคุณของโดเมนจริงถึงองค์ประกอบทั้งหมดในระยะเวลา จำกัด หากคุณกำลังพยายามเปลี่ยนอคติจากIntegerเป็นIntegerใช้[0,1 ..] ++ [-1,-2 ..]จะไม่ได้ผลเพราะคุณจะไม่ได้รับค่าลบ รูปธรรมก็inv ([0,1 ..] ++ [-1,-2 ..]) (+1) (-3)จะไม่ให้ผลคุ้มค่า

แต่จะทำงานในขณะที่วิ่งผ่านทางนี้จำนวนเต็มในลำดับต่อไป0 : concatMap (\x -> [x,-x]) [1..] [0,1,-1,2,-2,3,-3, and so on]แน่นอนinv (0 : concatMap (\x -> [x,-x]) [1..]) (+1) (-3)ทันทีส่งกลับ-4!

การควบคุมMonad.Omegaแพคเกจสามารถช่วยให้คุณทำงานผ่านรายการของ tuples เป็นต้นในทางที่ดี; ฉันแน่ใจว่ามีแพ็คเกจแบบนั้นมากกว่านี้ - แต่ฉันไม่รู้


แน่นอนว่าแนวทางนี้ค่อนข้างคิ้วต่ำและดุร้ายไม่ต้องพูดถึงน่าเกลียดและไร้ประสิทธิภาพ! ดังนั้นฉันจะปิดท้ายด้วยข้อสังเกตบางส่วนในส่วนสุดท้ายของคำถามของคุณเกี่ยวกับวิธีการ "เขียน" อคติ ระบบประเภทของ Haskell ไม่ได้ขึ้นอยู่กับการพิสูจน์ว่าฟังก์ชั่นเป็น bijection - คุณต้องการบางอย่างเช่น Agda สำหรับสิ่งนั้น - แต่ยินดีที่จะไว้วางใจคุณ

(คำเตือน: โค้ดที่ยังไม่ทดลองติดตาม)

ดังนั้นคุณสามารถกำหนดประเภทข้อมูลBijectionระหว่างประเภทaและb:

data Bi a b = Bi {
    apply :: a -> b,
    invert :: b -> a 
}

พร้อมกับค่าคงที่ให้มากที่สุด (ซึ่งคุณสามารถพูดว่า 'ฉันรู้ว่าพวกมันเป็นอคติ!') ตามที่คุณต้องการเช่น:

notBi :: Bi Bool Bool
notBi = Bi not not

add1Bi :: Bi Integer Integer
add1Bi = Bi (+1) (subtract 1)

และตัวผสมอัจฉริยะสองสามตัวเช่น:

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)

ผมคิดว่าคุณก็จะทำและได้รับinvert (mapBi add1Bi) [1,5,6] [0,4,5]หากคุณเลือกตัวรวมของคุณด้วยวิธีที่ชาญฉลาดฉันคิดว่าจำนวนครั้งที่คุณจะต้องเขียนBiค่าคงที่ด้วยมืออาจค่อนข้าง จำกัด

ท้ายที่สุดถ้าคุณรู้ว่าฟังก์ชั่นคือการคาดคะเนคุณหวังว่าจะมีภาพร่างพิสูจน์ของความจริงนั้นอยู่ในหัวของคุณซึ่งไอโซมอร์ฟิซึมของ Curry-Howard ควรจะสามารถเปลี่ยนเป็นโปรแกรมได้ :-)


6

เมื่อไม่นานมานี้ฉันได้จัดการกับปัญหาเช่นนี้และไม่ฉันจะบอกว่า (ก) ในหลาย ๆ กรณีไม่ใช่เรื่องยาก แต่ (ข) มันไม่มีประสิทธิภาพเลย

โดยพื้นฐานแล้วสมมติว่าคุณมีf :: a -> bและนั่นfคือความกล้าหาญ คุณสามารถคำนวณค่าผกผันได้f' :: b -> aอย่างโง่เขลา:

import Data.List

-- | Class for types whose values are recursively enumerable.
class Enumerable a where
    -- | Produce the list of all values of type @a@.
    enumerate :: [a]

 -- | Note, this is only guaranteed to terminate if @f@ is a bijection!
invert :: (Enumerable a, Eq b) => (a -> b) -> b -> Maybe a
invert f b = find (\a -> f a == b) enumerate

หากfเป็นการคาดคะเนและenumerateสร้างคุณค่าทั้งหมดอย่างแท้จริงaในที่สุดคุณก็จะได้รับaสิ่งf a == bนั้น

ประเภทที่มีBoundedและEnumอินสแตนซ์สามารถสร้างได้RecursivelyEnumerableเล็กน้อย Enumerableสามารถทำคู่ประเภทEnumerable:

instance (Enumerable a, Enumerable b) => Enumerable (a, b) where
    enumerate = crossWith (,) enumerate enumerate

crossWith :: (a -> b -> c) -> [a] -> [b] -> [c]
crossWith f _ [] = []
crossWith f [] _ = []
crossWith f (x0:xs) (y0:ys) =
    f x0 y0 : interleave (map (f x0) ys) 
                         (interleave (map (flip f y0) xs)
                                     (crossWith f xs ys))

interleave :: [a] -> [a] -> [a]
interleave xs [] = xs
interleave [] ys = []
interleave (x:xs) ys = x : interleave ys xs

เช่นเดียวกันกับการแยกEnumerableประเภท:

instance (Enumerable a, Enumerable b) => Enumerable (Either a b) where
    enumerate = enumerateEither enumerate enumerate

enumerateEither :: [a] -> [b] -> [Either a b]
enumerateEither [] ys = map Right ys
enumerateEither xs [] = map Left xs
enumerateEither (x:xs) (y:ys) = Left x : Right y : enumerateEither xs ys

ความจริงที่ว่าเราสามารถทำได้ทั้งสำหรับ(,)และEitherอาจหมายความว่าเราสามารถทำได้สำหรับประเภทข้อมูลเกี่ยวกับพีชคณิตใด ๆ


5

ไม่ใช่ทุกฟังก์ชันจะมีการผกผัน หากคุณ จำกัด การสนทนาไว้ที่ฟังก์ชันแบบตัวต่อตัวความสามารถในการสลับฟังก์ชันตามอำเภอใจจะให้ความสามารถในการถอดรหัสระบบเข้ารหัสใด ๆ เราต้องหวังว่าสิ่งนี้จะเป็นไปไม่ได้แม้ในทางทฤษฎี!


13
ระบบเข้ารหัสใด ๆ (ไม่รวมสิ่งแปลก ๆ บางอย่างเช่นแผ่นรองครั้งเดียวซึ่งไม่สามารถทำได้ด้วยเหตุผลอื่น ๆ ) สามารถถูกทำลายได้ด้วยพลังเดรัจฉาน นั่นไม่ได้ทำให้พวกมันมีประโยชน์น้อยลงและก็ไม่ใช่ฟังก์ชันการผกผันที่มีราคาแพงจนแทบทำไม่ได้

มันจริงเหรอ? หากคุณคิดว่าฟังก์ชันการเข้ารหัสString encrypt(String key, String text)โดยไม่มีคีย์คุณจะไม่สามารถทำอะไรได้ แก้ไข: บวกกับสิ่งที่เดลแนนพูด
mck

@MaciekAlbin ขึ้นอยู่กับรูปแบบการโจมตีของคุณ ตัวอย่างเช่นการโจมตีแบบข้อความธรรมดาที่เลือกอาจอนุญาตให้มีการแยกคีย์ซึ่งจะทำให้สามารถโจมตีข้อความรหัสอื่นที่เข้ารหัสด้วยคีย์นั้นได้

โดย "เป็นไปได้" ฉันหมายถึงบางสิ่งที่สามารถทำได้ในระยะเวลาที่เหมาะสม ฉันไม่ได้หมายถึง "คำนวณได้" (ฉันค่อนข้างแน่ใจ)
Jeffrey Scofield

@JeffreyScofield ฉันเห็นประเด็นของคุณ แต่ฉันต้องบอกว่าฉันสับสนกับ "ความเป็นไปได้ในทางทฤษฎี" - ความเป็นไปได้ (นิยามของ) ความเป็นไปได้ของเราไม่ได้อ้างถึงความยากลำบากในการทำจริงหรือ?

5

ในบางกรณีอาจเป็นไปได้ที่จะหาค่าผกผันของฟังก์ชัน bijective โดยการแปลงเป็นการแสดงสัญลักษณ์ จากตัวอย่างนี้ฉันเขียนโปรแกรม Haskell นี้เพื่อค้นหาการผกผันของฟังก์ชันพหุนามง่ายๆ:

bijective_function x = x*2+1

main = do
    print $ bijective_function 3
    print $ inverse_function bijective_function (bijective_function 3)

data Expr = X | Const Double |
            Plus Expr Expr | Subtract Expr Expr | Mult Expr Expr | Div Expr Expr |
            Negate Expr | Inverse Expr |
            Exp Expr | Log Expr | Sin Expr | Atanh Expr | Sinh Expr | Acosh Expr | Cosh Expr | Tan Expr | Cos Expr |Asinh Expr|Atan Expr|Acos Expr|Asin Expr|Abs Expr|Signum Expr|Integer
       deriving (Show, Eq)

instance Num Expr where
    (+) = Plus
    (-) = Subtract
    (*) = Mult
    abs = Abs
    signum = Signum
    negate = Negate
    fromInteger a = Const $ fromIntegral a

instance Fractional Expr where
    recip = Inverse
    fromRational a = Const $ realToFrac a
    (/) = Div

instance Floating Expr where
    pi = Const pi
    exp = Exp
    log = Log
    sin = Sin
    atanh = Atanh
    sinh = Sinh
    cosh = Cosh
    acosh = Acosh
    cos = Cos
    tan = Tan
    asin = Asin
    acos = Acos
    atan = Atan
    asinh = Asinh

fromFunction f = f X

toFunction :: Expr -> (Double -> Double)
toFunction X = \x -> x
toFunction (Negate a) = \a -> (negate a)
toFunction (Const a) = const a
toFunction (Plus a b) = \x -> (toFunction a x) + (toFunction b x)
toFunction (Subtract a b) = \x -> (toFunction a x) - (toFunction b x)
toFunction (Mult a b) = \x -> (toFunction a x) * (toFunction b x)
toFunction (Div a b) = \x -> (toFunction a x) / (toFunction b x)


with_function func x = toFunction $ func $ fromFunction x

simplify X = X
simplify (Div (Const a) (Const b)) = Const (a/b)
simplify (Mult (Const a) (Const b)) | a == 0 || b == 0 = 0 | otherwise = Const (a*b)
simplify (Negate (Negate a)) = simplify a
simplify (Subtract a b) = simplify ( Plus (simplify a) (Negate (simplify b)) )
simplify (Div a b) | a == b = Const 1.0 | otherwise = simplify (Div (simplify a) (simplify b))
simplify (Mult a b) = simplify (Mult (simplify a) (simplify b))
simplify (Const a) = Const a
simplify (Plus (Const a) (Const b)) = Const (a+b)
simplify (Plus a (Const b)) = simplify (Plus (Const b) (simplify a))
simplify (Plus (Mult (Const a) X) (Mult (Const b) X)) = (simplify (Mult (Const (a+b)) X))
simplify (Plus (Const a) b) = simplify (Plus (simplify b) (Const a))
simplify (Plus X a) = simplify (Plus (Mult 1 X) (simplify a))
simplify (Plus a X) = simplify (Plus (Mult 1 X) (simplify a))
simplify (Plus a b) = (simplify (Plus (simplify a) (simplify b)))
simplify a = a

inverse X = X
inverse (Const a) = simplify (Const a)
inverse (Mult (Const a) (Const b)) = Const (a * b)
inverse (Mult (Const a) X) = (Div X (Const a))
inverse (Plus X (Const a)) = (Subtract X (Const a))
inverse (Negate x) = Negate (inverse x)
inverse a = inverse (simplify a)

inverse_function x = with_function inverse x

ตัวอย่างนี้ใช้ได้กับนิพจน์เลขคณิตเท่านั้น แต่อาจเป็นข้อมูลทั่วไปในการทำงานกับรายการได้เช่นกัน


4

ไม่ไม่ใช่ฟังก์ชันทั้งหมดจะมีการผกผัน ตัวอย่างเช่นค่าผกผันของฟังก์ชันนี้จะเป็นอย่างไร?

f x = 1

ฟังก์ชันของคุณเป็นค่าคงที่ในที่นี้จะเกี่ยวกับฟังก์ชันทางชีวภาพ
Soleil - Mathieu Prévot
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.