【问题标题】:Why does a partial function applied to 2 functions that do the same thing defined in 2 different ways return a different result?为什么将偏函数应用于以 2 种不同方式定义的执行相同操作的 2 个函数会返回不同的结果?
【发布时间】:2025-12-15 09:40:01
【问题描述】:

我可以定义以下两个函数:

def add(a: Int, b: Int, c: Int) = a + b + c

这导致

add: (a: Int, b: Int, c: Int)Int

val add2 = (a: Int, b: Int, c: Int) => a + b + c

这会导致

add2: (Int, Int, Int) => Int = <function3>

这两个都是做同样事情但以不同方式定义的函数,我不明白的是,如果我继续定义一个部分应用的函数,如下所示:

def a = add _

这会导致

a: (Int, Int, Int) => Int = <function3> 

正如预期的那样,一个接受 3 个参数并返回一个 Int 的函数,但如果我这样做了

def a2 = add2 _

这会导致

a2: () => (Int, Int, Int) => Int = <function0>

这似乎是一个不带参数的函数,并返回一个带3个Int参数并返回一个Int的函数。为什么会这样?有人可以解释一下发生了什么吗?

谢谢

【问题讨论】:

  • 该副本是第三个相关链接。真的,请尝试寻找以前提出的问题——这个问题已经被问过很多次了。虽然我认为该链接是最接近的副本,但我个人建议我的 this answer 深入了解主题。
  • 哦,顺便说一句,如果你写def add2而不是val add2,也许你会更清楚。
  • 刚刚从一个已删除的答案中注意到您最初写的是 def 而不是 val。嗯,defval 的区别在于,前者每次调用时计算值,而后者计算一次,调用时返回预先计算的值。

标签: scala


【解决方案1】:

这是 Scala 的一个奇怪的副作用,它实际上没有(用户可访问的)字段,而是对所有内容都有访问器(getter)。观察:

scala> val i = 1
i: Int = 1

scala> i _
res0: () => Int = <function0>

原因是i 实际上是底层(隐藏的、不可访问的)字段的访问器 (def i: Int)。由于它只是一个方法,_ 会将其转换为一个函数。访问器不带任何参数,这就是为什么你有一个不带参数的函数。

【讨论】:

    【解决方案2】:

    Scala 既有函数又有方法,它们并不完全相同。

    def add(a: Int, b: Int, c: Int) = a + b + c
    

    其中定义了一个方法(不是函数!!)。

    val add2 = (a: Int, b: Int, c: Int) => a + b + c
    

    为 add2 分配了一个函数值(不是方法!!)。

    方法不能是最终值,而函数可以:

    scala> add
    <console>:9: error: missing arguments for method add;
    follow this method with `_' if you want to treat it as a partially applied function
                  add
                  ^
    
    scala> add2
    res1: (Int, Int, Int) => Int = <function3>
    
    scala> val a = add
    <console>:8: error: missing arguments for method add;
    follow this method with `_' if you want to treat it as a partially applied function
           val a = add
                   ^
    
    scala> val a2 = add2
    a2: (Int, Int, Int) => Int = <function3>
    

    在方法名后加下划线可以显式地将方法转为函数:

    scala> add _
    res2: (Int, Int, Int) => Int = <function3>
    

    但是如果你在一个值后面写下划线,它会转换成一个不带参数的函数,返回类型是它的类型:

    scala> val s = ""
    s: String = ""
    
    scala> val i = 1
    i: Int = 1
    
    scala> s _
    res3: () => String = <function0>
    
    scala> i _
    res4: () => Int = <function0>
    

    所以如果值本身是一个函数,在它后面写下划线会得到一个新函数,它不带参数,返回类型为函数:

    scala> add2 _
    res5: () => (Int, Int, Int) => Int = <function0>
    

    【讨论】:

      【解决方案3】:

      Rex Kerr's answer 中清楚地描述了效果,但您应该注意,add2 是已经代表部分应用功能的值,例如代表。 为了演示,您可以比较 add1 _ 表达式和 add2 值的类型。

      scala> def getManifest[T](x:T)(implicit m:scala.reflect.Manifest[T]) = m
      getManifest: [T](x: T)(implicit m: scala.reflect.Manifest[T])scala.reflect.Manifest[T]
      
      scala> getManifest(add1 _) == getManifest(add2)
      res14: Boolean = true
      

      【讨论】:

        最近更新 更多