接受函数或值的Haskell函数,然后调用函数或返回值

时间:2015-05-15 03:38:13

标签: haskell functional-programming

如何在Haskell中编写一个类型声明和函数,它接受一个函数(它本身不带参数)一个值。当给定一个函数时,它调用该函数。给定值时,它返回值。

[edit] 为了提供更多上下文,我很好奇如何在Haskell中解决这个问题,而不会有点麻烦:Designing function f(f(n)) == -n

肖恩

5 个答案:

答案 0 :(得分:5)

  

我非常好奇如何在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

现在,如果您看到这些等式的左侧,那么您会注意到有四种情况:

  1. f x
  2. f y
  3. f (-x)
  4. f (-y)
  5. 请注意,函数f的域分为正数和负数,x(-x),以及y(-y)。我们假设xy一起形成正数集,(-x)(-y)一起形成负数集。

    这组正数分为两个proper disjoint个子集,xy。我们如何将正数集合分成两个正确的不相交子集?奇数和偶数是一个很好的候选人。因此,我们假设x是正奇数的集合,而y是正偶数集。

    使用奇数和偶数的另一个优点是,当否定奇数时,奇数仍为奇数,偶数仍为偶数。因此,(-x)是负奇数的集合,(-y)是负偶数集。

    现在,再考虑四个案例。请注意,只有当数字为偶数时,符号才会改变:

    1. f x = y(标志不会改变)。
    2. f y = (-x)(签名更改)。
    3. f (-x) = (-y)(标志不会改变)。
    4. f (-y) = x(签名更改)。
    5. 因此,我们只在偶数时(即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 = 0 if and only if n = 0。因此,让我们考虑01(-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
      

      希望有所帮助。

答案 1 :(得分:4)

您不能编写具有两个不同签名的函数(除非您使用类型类,但类型类不适合此问题)。您必须以允许您将函数和非函数值视为相同类型的方式解决此问题。有两个明显的选择:

  1. 使用和类型。

    f :: Either (Int -> Char) Char -> Char
    f (Left g) = g 1
    f (Right c) = c
    
  2. 使用const将非函数值转换为忽略其参数的函数:

    f = ($ 42)
    f chr         --> '*'
    f (const 'a') --> 'a'
    
  3. 但是,由于这是一个非常难以理解的问题,我怀疑这是一个XY problem

答案 2 :(得分:2)

可以这样做:

data FunctionOrValue a
    = Function (() -> a)
    | Value a

getValue :: FunctionOrValue a -> a
getValue (Function f) = f ()
getValue (Value x) = x

然而这有点傻。

听起来你正在尝试手动推迟值,但由于Haskell很懒,所以通常不需要这样做。

答案 3 :(得分:1)

基于您发布的面试问题的答案:

f n = if (abs fracN) > 1 then 1/fracN else - (1/fracN)
  where
    fracN = realToFrac n

问题指出输入是一个int;它没有指定结果也必须是int。

编辑:如果必须返回Int,请注意该问题允许您指定一系列可能的输入。我使用限制1073741823(签名的32位int的最大值的一半),这允许我写这个:

fint :: Int -> Int
fint 0 = 0
fint n = if (abs n) <= rangeVal then n+addend else -(n-addend)
  where
    rangeVal = 1073741823
    negator = if n < 0 then -1 else 1
    addend = negator*rangeVal  

答案 4 :(得分:1)

Haskell(恕我直言)中的一个好处是,在没有参数返回值的情况下,值和函数之间没有区别(感谢懒惰和纯度)。或者如果您愿意,每个值实际上都是一个没有参数的函数,在需要时会对其进行评估。因此,无需担心这类问题。没有像f()这样的东西,它只是f

例如,你可以写

x = 3 :: Int 
f = head [] :: Int -- should blow up but doesn't

head [x, f] -- note that f and x have the same type
> 3  -- doesn't blow up on f, because f is not called

head [f] -- blows up, when trying to print f