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

Haskell function that accepts function or value, then calls function or returns value

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

[edit] 为了提供更多上下文,我主要好奇如何在 Haskell 中解决这个问题而不需要一点点操作:Designing function f(f(n)) == -n

肖恩

可以这样做:

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

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

不过这有点傻。

听起来您正在尝试手动延迟值,但由于 Haskell 是懒惰的,通常不需要这样做。

你不能写一个函数有两个不同的签名(除非你使用类型类,但类型类不适合这个问题)。您必须以一种让您将函数值和非函数值视为同一类型的方式来解决这个问题。有两个明显的选择:

  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'
    

但是,由于这是一个非常不合理的要求,我怀疑这是一个 XY problem.

基于您发布的采访问题的答案:

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

题目指定输入是int;它没有指定结果也必须是一个整数。

编辑:如果您必须 return Int,请注意该问题允许您指定可能的输入范围。我使用 1073741823 的限制(带符号的 32 位整数的最大值的一半),这让我可以这样写:

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  

I'm mostly curious how to solve this problem in Haskell without bit twiddling: 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).

注意函数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(符号更改)。

因此,我们只对偶数取反(即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

希望对您有所帮助。

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