10

scala_3_方法调用

 3 years ago
source link: https://zhuanlan.zhihu.com/p/37408818
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

scala_3_方法调用

习惯了在IDEA中用scala worksheet中写一点笔记,就是当成markdown一样用了.知乎对markdown的解析也和翔一样,所以排版不是很好,见谅.后续有时间会放出所有markdown的原件.

def & val

  • def是在调用时evaluate,每次都是一个新的函数,val是表达式,定义时就已经evaluate。从这一点上说,def和val是完全不同的,def类似一个工厂方法,生成功能相同的不同函数实例.
def even: Int => Boolean = _ % 2 == 0  //def定义的函数
even eq even //false   两次调用生成不同实例
val even2: Int => Boolean = _ % 2 == 0 
even2 eq even2 //true 只有一个实例

//进一步在worksheet验证,下面两行,第一行返回m: m[] => Int => Int;
//第二行报错提示方法没有实现
def m: Int => Int = ??? //??? 表示未实现方法,注意不能和 = 贴近 
val f:Int=>Int = ???  //scala.NotImplementedError: an implementation is missing

//可以通过lazy避免
lazy val lfn:Int=>Int = ???
  • 在scala中,def和val都可以定义函数,同时def还可以定义方法.
  • 方法和函数的区别是很微妙的,绝大多数时候你不需要区分对待(因为scala会处理好). 函数是FucntioN trait的一个实例,编译后是独立的class文件(function is value,is an object of FunctionN,which has apply method),作为一个value,自然可以作为实参传递给方法/函数调用;而方法是依附于对象的,这个和java没有多大区别;方法调用时形式为obj.mtd(param)的格式或者 o m p 这种中缀操作符形式,而函数调用则是fn(p1,p2)的形式,本质上是调用了Function2 trait的apply()方法.
//上面已经定义定义了函数even/even2,因为函数是object,所以可以调用它的方法
even2.toString() 
even.toString()
//而方法是不能调用toString方法的
def m(p:String) :String ={
    p+"fix"
}
m.toString //提示m没有实参,你需要先调用方法然后才能调用toString
//如果m没有形参,那么则方法调用可以省略(),会误以为是m对象的toString方法
  • 你可以将方法转换为函数
def m1(p:Int):String={ "fix"}   
val f1 = m1 _  //下划线表示参数列表 eta-expansion 

初学者经常对scala中方法调用的()和{}互换的规则感到困惑.其实牢记三个规则即可理清:

  1. 规则1:{}表示code block,你可以在里面放几乎任何语句,block的返回值是由最后一句决定
  2. 规则2:block内容如果只有一句则可以省略{},但是case clause除外:{case ...}
  3. 规则3: 单参数方法如果实参是code block,那么可以省略()

下面看下常见的调用形式:

// 规则1
{
  import util.Try
  println{3;"hello"} //规则3
  5
}

val tupleList = List[(String, String)]()
tupleList takeWhile({case(t1,t2) => t1==t2}) //基本形式
tupleList takeWhile {case(t1,t2) => t1==t2} //规则3: takeWhile接受一个参数,正好传递个block实参,省略()
tupleList takeWhile(case(t1,t2) => t1==t2) //报错,规则2:case clause不能省略{}

List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft(_+_) //规则2 

// 一种特殊情况,提示:隐式转换
val r = List(1, 2, 3).foldLeft(0) _+_ //r: String => String = $Lambda$1426/749905080@1df3ad9
val l = r{"hello"} //l: String = $Lambda$1427/1442564759@f8821d8hello
//说明以下,上面的情况是随意省略括号带来导致的问题,
//foldLeft由于curry化,两个参数可以分开用两个()传递实参,
//但是如果将第二个()认为是单参数并且实参是单句block 而省略掉 _+_的{},则会被编译器解析错误. 
//实际上scalac会认为上面的语句是下面这种格式:
val r = (List(1, 2, 3).foldLeft(0) _ ) + _
//这样就可以理解了吧


//补充一条规则

//使用{}的特殊情况:for推导可以和()互换,一般建议是除了yield的其他情况都用()
for{tpl <-tupleList} yield tpl._2

//不建议
for{tpl <-tupleList} {
  println(tpl)
}
//推荐
for(tpl <-tupleList) {
  println(tpl)
}

//补充:方法定义时如果没有返回值可以省略=,称为procedure,scala 2.13已经废弃,不要这么写
def p(in:String ){
  println(s"hello $in")
}
//正确的应该是:
def p1(in:String ): Unit ={
  println(s"hello $in") 
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK