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

http://www.jianshu.com/p/93eefcb61d4f

val和def的区别

在scala中,可以用val和def前缀来定义变量,例如:

 val  x = 1
 def  y = “foo”

这两者的区别在于:

  • val定义值时,会做call-by-value操作,
  • def则会做call-by-name操作。

例如:

 // 这是一个死循环
 def loop: Boolean = loop

 // 用val定义时会做call-by-value,以下语句会block住
 val x = loop 

 // 用def定义时,是做的call-by-name。故以下语句暂时不会执行,在用到y的时候才做evaluation
 def y = loop

陷到loop里时,用ctrl+c发中断信号终止。

函数定义中的call-by-name和call-by-value

首先思考一个问题,如何在不使用”||”和“&&”的情况下,实现如下两个函数and和or:

 and(x, y) == x && y
 or(x, y) == x || y

对于and,可以用如下方式定义:

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

 // 测试
 and(true, true)      // => true
 and(false, true)     // => false

但是,这种定义在遇上循环的时候会有问题,例如

 and(false, loop)

上述语句会陷入循环,因为在我们的定义中,对于x和y参数的引用都是call-by-value的。要指定参数为call-by-name,只需要把函数定义改为如下形式即可:

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

 // 测试
 and(false, loop)     // => false

注意我们在定义参数y的时候,使用的=>符号。

参考:
[1] https://class.coursera.org/progfun-003/lecture/5


<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 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值