我很好奇如何在 Haskell 中解决这个问题而无需费力:Designing function f(f(n)) == -n
这其实很容易解决:
when :: (a -> Bool) -> (a -> a) -> a -> a
when p f x = if p x then f x else x
f :: Integer -> Integer
f = (+) <$> when even negate <*> signum
我们如何得出这个?考虑:
f (f n) = (-n) -- (0) - from the interview question
f x = y -- (1) - assumption
f y = (-x) -- (2) - from (0) and (1), f (f x) = (-x)
f (-x) = (-y) -- (3) - from (0) and (2), f (f y) = (-y)
f (-y) = x -- (4) - from (0) and (3), f (f (-x)) = x
现在,如果您看到这些等式的左侧,您会注意到有四种情况:
-
f x。
-
f y。
-
f (-x)。
-
f (-y)。
注意函数f的域分为正数和负数,x和(-x),以及y和(-y)。假设x 和y 一起构成正数集,(-x) 和(-y) 一起构成负数集。
正数集合分为properdisjoint两个子集x和y。我们如何将一组正数分成两个适当的不相交子集?奇数和偶数都是不错的选择。因此,假设x 是正奇数的集合,y 是正偶数的集合。
使用奇数和偶数的另一个优点是,当求反时,奇数保持奇数,偶数保持偶数。因此,(-x) 是负奇数的集合,(-y) 是负偶数的集合。
现在,再次考虑这四种情况。请注意,仅当数字为偶数时符号才会改变:
-
f x = y(符号不变)。
-
f y = (-x)(符号更改)。
-
f (-x) = (-y)(符号不变)。
-
f (-y) = x(符号更改)。
因此,我们只在偶数时取反(即when even negate)。
接下来,我们需要将奇数转换为偶数,反之亦然。最简单的方法是在数字中加或减一。但是,应注意结果数字不是0。考虑0的特殊情况:
f 0 = z -- (a) - assumption
f z = (-0) -- (b) - from (0) and (a), f (f 0) = (-0)
f (-0) = (-z) -- (c) - from (0) and (b), f (f z) = (-z)
(-0) = 0 -- (d) - reflexivity
f (-0) = f 0 -- (e) - from (d)
(-z) = z -- (f) - from (a) and (c) and (e)
z = 0 -- (g) - from (d) and (f)
f 0 = 0 -- (h) - from (a) and (g)
因此,f n = 0if and only ifn = 0。所以让我们考虑0、1 和(-1) 的邻居。这两个都是奇数。因此,它们没有被否定。但是,它们确实需要转换为偶数(0 除外)。因此,1 被转换为2,(-1) 被转换为(-2)。
因此,对于奇数,我们只需将数字的符号添加到数字本身。
现在,考虑偶数。我们知道:
f 1 = 2 -- (A)
f (-1) = (-2) -- (B)
因此:
f 2 = (-1) -- (C), from (0) and (A), f (f 1) = (-1)
f (-2) = 1 -- (D), from (0) and (B), f (f (-1)) = 1
我们知道偶数总是被否定的。因此,2 首先变为(-2),反之亦然。设原来的偶数为n。因此,首先我们negate n,然后添加signum n:
evenF n = negate n + signum n
evenF 2 = negate 2 + signum 2
= (-2) + 1
= (-1)
evenF (-2) = negate (-2) + signum (-2)
= 2 + (-1)
= 1
evenF 0 = negate 0 + signum 0
= 0 + 0
= 0
因此,对于奇数和偶数,我们将原始数字的符号添加到when even negate。因此,f 定义为:
f :: Integer -> Integer
f = (+) <$> when even negate <*> signum
希望对您有所帮助。