数字作为乘法函数(奇怪但有趣) [英] Numbers as multiplicative functions (weird but entertaining)

查看:155
本文介绍了数字作为乘法函数(奇怪但有趣)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Haskell中的默认函数组合的问题的评论 ,人们提到制作 Num < a - >的实例

  { -  ,所以我想用函数表示法来表示乘法: #LANGUAGE TypeFamilies# - } 
import Control.Applicative

instance显示(a-> r)其中 - 在最近的GHC版本中不需要
show f =一个函数

实例等式(a-> r)其中 - 在最近的GHC版本中不需要
f == g =错误抱歉,哈斯克尔,我撒谎,我无法真正比​​较相等函数

实例(Num r,a〜r)=> Num(a - > r)其中
(+)= liftA2(+)
( - )= liftA2( - )
(*)= liftA2(*)
abs = liftA abs
negate = liftA否定
signum = liftA signum
fromInteger a =(fromInteger a *)

请注意,fromInteger定义意味着我可以编写计算结果为12的 3 4 ,并且 7(2+ 8)是70,就像你希望的那样。

然后,这一切都变得奇妙,有趣的奇怪!如果可以的话,请解释一下这个奇怪的问题:

  * Main> 1 2 3 
18
* Main> 1 2 4
32
* Main> 1 2 5
50
* Main> 2 2 3
36
* Main> 2 2 4
64
* Main> 2 2 5
100
* Main> (2 3)(5 2)
600



<编辑:使用Applicative而不是Monad因为Applicative通常是很好的,但它对代码没有太大的区别。]

解决方案

2 3 4 包含您的实例,它们都是 2 3 是功能。因此 2 实际上是(2 *),并且类型为 Num a => a - >一个 3 是一样的。 2 3 然后是(2 *)(3 *),它与 2相同*(3 *)。在你的实例中,这是 liftM2(*)2(3 *)然后是 liftM2(*)(2 *)(3 *)。现在这个表达式没有任何实例。



那么这是什么意思?那么, liftM2 对于函数来说是一种双重组合。特别是, liftM2 f g h \ x - >相同。 f(g x)(h x)。因此, liftM2(*)(2 *)(3 *)就是 \ x - > (*)((2 *)x)((3 *)x)。简化一下,我们得到: \ x - > (2 * x)*(3 * x)。所以现在我们知道 2 3 4 实际上是(2 * 4)*(3 * 4) p>

现在为什么函数以这种方式工作 liftM2 ?让我们看看( - >)r 的monad实例(记住( - >)r (r - >),但我们不能编写类型级别的操作符部分):

  instance Monad(( - >)r)其中
return x = \_ - > x
h>> = f = \ w - > f(hw)w

所以 return 常量>> = 有点奇怪。我认为用加入来看这很容易。对于函数, join 的工作方式如下所示:

  join f = \ x  - > fxx 

也就是说,它接受两个参数的函数并将其转换为一个参数的函数使用这个论点两次。够简单。这个定义也是有道理的。对于函数, join 必须将两个参数的函数变成一个函数;



>> = fmap ,后跟加入。对于函数, fmap 只是(。)。所以现在>> = 等于:

  h> ;> = f = join(f。h)

这就是:

  h>> = f = \ x  - > (f。h)xx 

现在我们摆脱 code>得到:

  h>> = f = \ x  - > f(hx)x 

现在我们知道>> = 的作品,我们可以看看 liftM2 liftM2 的定义如下:

  liftM2 fab = a>> ; = \ a' - > b>> = \ b' - >返回(f a'b')

我们可以简单地点点滴滴。首先, return(f a'b')变成 \ _ - > f a'b'。结合 \ b' - > ,我们得到: \ b'_ - > f a'b'。然后 b>> = \ b'_ - > f a'b'变为:

  \ x  - > (\ b'_  - > f a'b')(bx)x 

第二个 x 被忽略,我们得到: \ x - > (\ b' - > f a'b')(b x),然后将其减至 \ x - > f a'(b x)。所以这给我们留下了:

  a>> = \ a' - > \ x  - > f a'(bx)

再次,我们用>> =

  \ y  - > (\ a'x  - > f a'(bx))(ay)y 

简化为:

  \ y  - > f(ay)(by)

这正是我们用作 liftM2 早些时候!



希望现在 2 3 4 的行为完全有意义。 / p>

In the comments of the question Tacit function composition in Haskell, people mentioned making a Num instance for a -> r, so I thought I'd play with using function notation to represent multiplication:

{-# LANGUAGE TypeFamilies #-}
import Control.Applicative

instance Show (a->r) where   -- not needed in recent GHC versions
  show f = " a function "

instance Eq (a->r) where     -- not needed in recent GHC versions
  f == g = error "sorry, Haskell, I lied, I can't really compare functions for equality"

instance (Num r,a~r) => Num (a -> r) where
  (+) = liftA2 (+)
  (-) = liftA2 (-)
  (*) = liftA2 (*)
  abs = liftA abs
  negate = liftA negate
  signum = liftA signum
  fromInteger a = (fromInteger a *)

Note that the fromInteger definition means I can write 3 4 which evaluates to 12, and 7 (2+8) is 70, just as you'd hope.

Then it all goes wonderfully, entertainingly weird! Please explain this wierdness if you can:

*Main> 1 2 3
18
*Main> 1 2 4
32
*Main> 1 2 5
50
*Main> 2 2 3
36
*Main> 2 2 4
64
*Main> 2 2 5
100
*Main> (2 3) (5 2)
600

[Edit: used Applicative instead of Monad because Applicative is great generally, but it doesn't make much difference at all to the code.]

解决方案

In an expression like 2 3 4 with your instances, both 2 and 3 are functions. So 2 is actually (2 *) and has a type Num a => a -> a. 3 is the same. 2 3 is then (2 *) (3 *) which is the same as 2 * (3 *). By your instance, this is liftM2 (*) 2 (3 *) which is then liftM2 (*) (2 *) (3 *). Now this expression works without any of your instances.

So what does this mean? Well, liftM2 for functions is a sort of double composition. In particular, liftM2 f g h is the same as \ x -> f (g x) (h x). So liftM2 (*) (2 *) (3 *) is then \ x -> (*) ((2 *) x) ((3 *) x). Simplifying a bit, we get: \ x -> (2 * x) * (3 * x). So now we know that 2 3 4 is actually (2 * 4) * (3 * 4).

Now then, why does liftM2 for functions work this way? Let's look at the monad instance for (->) r (keep in mind that (->) r is (r ->) but we can't write type-level operator sections):

instance Monad ((->) r) where  
    return x = \_ -> x  
    h >>= f = \w -> f (h w) w  

So return is const. >>= is a little weird. I think it's easier to see this in terms of join. For functions, join works like this:

join f = \ x -> f x x

That is, it takes a function of two arguments and turns it into a function of one argument by using that argument twice. Simple enough. This definition also makes sense. For functions, join has to turn a function of two arguments into a function of one; the only reasonable way to do this is to use that one argument twice.

>>= is fmap followed by join. For functions, fmap is just (.). So now >>= is equal to:

h >>= f = join (f . h)

which is just:

h >>= f = \ x -> (f . h) x x

now we just get rid of . to get:

h >>= f = \ x -> f (h x) x

So now that we know how >>= works, we can look at liftM2. liftM2 is defined as follows:

liftM2 f a b = a >>= \ a' -> b >>= \ b' -> return (f a' b')

We can simply this bit by bit. First, return (f a' b') turns into \ _ -> f a' b'. Combined with the \ b' ->, we get: \ b' _ -> f a' b'. Then b >>= \ b' _ -> f a' b' turns into:

 \ x -> (\ b' _ -> f a' b') (b x) x

since the second x is ignored, we get: \ x -> (\ b' -> f a' b') (b x) which is then reduced to \ x -> f a' (b x). So this leaves us with:

a >>= \ a' -> \ x -> f a' (b x)

Again, we substitute >>=:

\ y -> (\ a' x -> f a' (b x)) (a y) y

this reduces to:

 \ y -> f (a y) (b y)

which is exactly what we used as liftM2 earlier!

Hopefully now the behavior of 2 3 4 makes sense completely.

这篇关于数字作为乘法函数(奇怪但有趣)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆