scala中call-by-name和call-by-value

本文探讨了Scala语言中val、var与def的区别,并详细解释了call-by-value与call-by-name的概念及其应用场景。通过具体示例展示了不同参数传递方式对函数行为的影响。
call-by-name:

执行对象的方法后者返回对象的属性,对象的句柄(引用)传递给调用方法,所有的操作都是通过这个句柄作用在这

个句柄指向的对象上,并没有制作副本。这样方法调用结束后,这个对象的状态是改变过的,不可恢复的

call-by-value:

制作传入的参数的一个副本,操作都是对这个副本进行的(基本类型都是call by value)


1、var,val和def的区别

首先看看下面定义的x,y,z:

class sheet1 {
val x = 1
var y = 2
def z = 3  
}
通过编译如下:

public class sheet1
{
  private final int x = 1;
  private int y = 2;

  public int x()
  {
    return this.x; } 
  public int y() { return this.y; } 
  public void y_$eq(int x$1) { this.y = x$1; } 
  public int z() { return 3;
  }
}
三者的区别:

1、var和val都会做call-by-value操作,而val 是用final修饰,也就是val是不可以改变的。

2、def是做call-by-name操作的。

3、def和val都是不可以改变的。

再如下:

class sheet1 {
  //死循环
def loop:Boolean = loop
//会做call-by-value,下面语句block住
val x = loop
var y = loop
//def 是call-by-name,当真正用到z的时候才去寻根
def z = loop
}




2、函数定义中call-by-value和call-by-name

用函数实现and,有如下定义:

def and(x: Boolean, y: Boolean) = 
      if (x) y else false
<pre name="code" class="plain">def loop:Boolean = loop


我们用调用and(true,loop),会发现会陷入死循环。原因是两个参数都是做call-by-value操作的,那么它就一直找一个loop值,就这样一直找下去。所以就无限期的循环。


我们把这个函数变成call-by-name操作,如下:

def and(x: Boolean, y: => Boolean) = 
      if (x) y else false
def loop:Boolean = loop

我们试一下 :and(false, loop)
scala> and(false, loop)
res0: Boolean = false<span style="font-weight: bold;">
</span>
发现可以正常运行,返回的是false,那么我们在试一下and(true, loop)
哭突然发现又陷入死循环。原因是:第二个参数是call-by-name,在一开始的时候,不会去用它,知道真正调

用的时候,就开始寻根了

注意:此刻y的定义为:y: => Boolean














<think>好的,我现在要回答用户关于Scala中值调用名调用的区别、使用场景及示例代码的问题。首先,我需要回忆一下Scala中这两种参数传递方式的定义特点。 值调用(Call-by-value)是按值传递参数,也就是说,在函数调用时,参数表达式会被计算,然后传入计算后的值。这意味着无论参数在函数体内被使用多少次,表达式只会计算一次。而名调用(Call-by-name)则是按名称传递,参数表达式不会立即计算,而是每次在函数体内被使用时才会重新计算。这可能会影响性能,尤其是当表达式计算代价高时,但同时也提供了延迟计算的灵活性。 接下来,我需要明确两者的区别。值调用在传入前计算一次,适用于表达式稳定且不需要重复计算的场景;名调用则延迟计算,适用于需要每次使用时都重新求值的情况,比如日志记录或者条件判断中的参数。 然后,我需要考虑使用场景。例如,名调用常用于构建自定义控制结构,比如实现一个自己的while循环,或者需要延迟计算以避免不必要的计算,如日志消息的生成。值调用则适用于大多数常规情况,尤其是当参数会被多次使用且不需要重复计算时。 关于示例代码,我需要分别展示值调用名调用的函数定义及调用方式。比如,定义一个值调用的函数,参数在传入前计算;再定义一个名调用的函数,参数在每次使用时计算。同时,可能还需要展示一个需要名调用的例子,比如避免在条件判断前计算参数,例如短路求值的逻辑。 另外,需要注意语法上的区别。值调用是标准的参数定义方式,而名调用需要在参数类型前加上“=>”,例如“x: => Int”。 还需要检查是否有相关的引用内容可以支持回答。用户提供的引用中有关于Scala函数可变参数、函数变量定义等内容,但可能没有直接提到值调用名调用。不过,引用[3]提到函数字面量的语法,可能与名调用的语法相关,可以作为参考。 最后,要确保回答结构清晰,分点说明区别、使用场景,并提供示例代码。同时,按照用户要求,生成相关问题,并正确添加引用标识。</think>### Scala值调用与名调用详解 #### 一、核心区别 1. **值调用(Call-by-value)** 参数在传入函数前会被**立即求值**,且仅计算一次。例如: ```scala def valueCall(x: Int): Unit = { println(x) println(x) } // 调用时先计算3+2=5,再传递结果 valueCall(3 + 2) // 输出两次5 ``` 2. **名调用(Call-by-name)** 参数在每次使用时才会**延迟求值**,表达式可能被多次计算。语法使用`=>`标记: ```scala def nameCall(x: => Int): Unit = { println(x) println(x) } // 每次使用x时重新计算3+2 nameCall(3 + 2) // 输出两次5,但每次重新计算(若表达式有副作用则不同) ``` #### 二、使用场景对比 | **场景** | **值调用** | **名调用** | |----------------------|-------------------------------------|-------------------------------------| | **表达式稳定性** | 表达式结果固定且计算成本高时 | 表达式可能变化或需延迟计算时 | | **性能影响** | 一次计算,适合重复使用 | 多次计算,需谨慎处理性能问题 | | **典型应用** | 常规函数参数传递 | 自定义控制结构(如`while`循环) | | **副作用处理** | 副作用在传入前触发 | 副作用在每次使用时触发 | #### 三、示例代码 1. **值调用示例** ```scala def logValue(msg: String): Unit = { if (debugMode) println(msg) // msg已被计算,即使未使用 } logValue(createComplexLog()) // createComplexLog()始终执行 ``` 2. **名调用示例** ```scala def logName(msg: => String): Unit = { if (debugMode) println(msg) // 仅在debugMode=true时计算msg } logName(createComplexLog()) // createComplexLog()可能不执行 ``` #### 四、注意事项 - **性能优化**:名调用可能导致重复计算(如循环中),需结合`lazy val`缓存结果: ```scala def safeCall(x: => Int) = { lazy val cached = x // 仅首次使用时计算 cached + cached } ``` - **短路逻辑**:名调用天然支持短路求值,例如自定义`&&`操作: ```scala def and(a: => Boolean, b: => Boolean): Boolean = if (a) b else false ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值