【问题标题】:What's the difference between .map (...) and .map {...} in scalascala 中的 .map (...) 和 .map {...} 有什么区别
【发布时间】:2017-08-24 01:23:00
【问题描述】:

我是 scala 的新手。高阶函数跟随大括号或括号块之间有什么区别吗?

例如:

  • List(1, 2, 3).map(i=> i + 1)

  • List(1, 2, 3).map {i => i + 1}

他们都得到相同的结果:List(2, 3, 4)

但是对于这个例子List(1, 2).map { println("Hi"); _ + 1 },结果如下,为什么'Hi'只打印一次?

Hi
List[Int] = List(2, 3)

【问题讨论】:

标签: scala


【解决方案1】:

Scala 中的块只是一个表达式。像括号一样,它们对于将代码组合在一起很有用。与括号不同,块不只包含一个表达式,而是可以包含 1 个或多个表达式。块的值是它最后一个表达式的值。

{ println("Hi"); _ + 1 } 等价于{ println("Hi"); (i: Int) => i + 1 }。这是一个打印出"Hi" 的块,它的值是一个加一的函数。该字符串在执行退出块之前打印出来,它产生的函数对println一无所知。

以下是这些规则的一些示例:

list.map(i => i + 1)
//      ^----------^
//    function literal passed as argument

list.map(_ + 1)
//       ^---^
// Underscore shorthand for above

list.map({ i => i + 1 })
// Identical to above.
// The block only contains one expression, so it has the value of that expression
// Otherwise stated: { expr } === expr

list.map({ println("Hi"); _ + 1 })
//         ^-----2-----^  ^-3-^
//       ^------------1---------^
// 1: The argument to map is the value of this block
// 2: The first statement of the block prints something. This is only executed once,
//   because it's not the *block* being passed as argument, it's its value.
// 3: Function literal in underscore notation. This is the value of the block
//   and this is what map sees.
// Order of operations (approx. bytecode):
// load list onto stack
// load string "Hi" onto stack
// call println and pop string off stack
// create function (i => i + 1) on top of stack
// invoke map with argument (i => i + 1), popping list and function off stack

list.map { println("Hi"); _ + 1 }
// Identical to above, but Scala lets you omit the () because you are using {}

list.map({ i => println("Hi"); i + 1 })
// Function literals grow as big as they can be.
// The block contains only one expression, which is (i => println("Hi"); i + 1)
// This function prints "Hi" and then returns i + 1
// This call to map will print "Hi" for every element

list.map { i => println("Hi"); i + 1 }
// Identical to above, but Scala lets you omit the () because you are using {}

此外,还有要处理的按名称参数。按名称参数声明如下:

def func(a: => String) // Not () => String, it's => String

当你有 by-name 参数时,那么

func { println("x"); _ + 1 }

实际上将整个块作为参数传递。该块仍评估为i => i + 1,但func 控制评估发生的时间。具体来说,就是把block的代码变成了Function0,传入func,可以任意调用多次,有副作用。这可以产生很大的效果,本质上允许普通函数像自定义控制流操作符一样工作:

@tailrec def repeat(i: Int)(op: => Any): Unit
= if(i == 0) ()
  else {
    require(i >= 0, s"negative repeat amount: $i")
    op // Evaluate op
    repeat(i - 1)(op) // Won't evaluate op; will let sub-call deal with it
  }

repeat(5) { println("Hi"); println("Bye") }
// Hi
// Bye
// Hi
// Bye
// Hi
// Bye
// Hi
// Bye
// Hi
// Bye

注意块周围的括号是如何省略的,这确实使这看起来像是定义控制流运算符的能力。

【讨论】:

  • 太好了!真的很有帮助。
  • 漂亮的 ascii 艺术!请说“结果表达式”而不是块的“返回值”。块不是函数,它是一堆语句,最后是结果。我对上面链接问题的回答也说“嗨”。 “你好。”关于list.map { x => } 的语法,请参阅stackoverflow.com/a/13873899/1296806
【解决方案2】:

一般情况下,您会使用括号括住简单的函数参数:

l.map( x => x * 2 )

和花括号用于包含更复杂的代码块或部分函数,​​包括case 模式匹配:

l.map{ x =>
  val y = x * 2
  y
}

l.map{
  case x if x%2 == 0 => x * 2
  case _ => 0
}

至于Hi只打印一次的原因,List(1, 2).map{ println("Hi"); _ + 1 }List(1, 2).map{ println("Hi"); x => x + 1 }没有区别。在map 迭代中包含println

List(1, 2).map{ x => println("Hi"); x + 1 }
// Hi
// Hi
// res1: List[Int] = List(2, 3)

【讨论】:

    猜你喜欢
    • 2016-12-24
    • 2023-03-04
    • 2018-03-08
    • 2014-11-20
    • 2011-10-05
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    • 1970-01-01
    相关资源
    最近更新 更多