美文网首页
《快学 Scala》 学习笔记 - Chapter 2

《快学 Scala》 学习笔记 - Chapter 2

作者: Zeal_8421 | 来源:发表于2017-01-07 23:50 被阅读118次

条件表达式

  • scala的表达式是有返回值的,和erlang一样,语句的最后一个表达式就是返回值

  • 可以使用 val s = if (x>0) 1 else -1 的方式声明变量,此处s 为 Int 类型

    • 也可以用 if (x>0) s=1 else s=-1 但前提是先要通过var 声明 s, 也就是说s就不能是常量了
  • val s=if(x>0) “asdfadf” else -1 的表达式中,s的类型为Any,因为两个表达式的值类型是不相同的,即混合类型,那么只能是他们的公共超类Any了

  • if (x>0) 1 则等同于 if (x>0) 1 else ()

    • () 等同于 java中的void

    • () 在scala 中的值为Unit,表示“无值”的值

  • scala终端里需要注意else的换行问题

    if (x > 0) 1  
    else if ( x == 0 ) 0 else -1  
    

    需要写成

    if (x > 0) { 1  
    } else if ( x == 0 ) 0 else -1
    
    • 在编译器中编译时是无需顾虑这个问题的

    • 可以通过:paste 模式将代码粘贴进去后统一解析执行,这样scala终端就不会近视了

语句终止

  • 正常多行时不需要分号,单行有多个表达式时需要分号

    • 但是如果不习惯不使用,则可以用,没什么坏处
  • s=s0+(v-v0)t+ // +告诉解析器这里不是语句的末尾
    0.5
    (a-a0)tt

    • 这里跟GO很像,符号放到最后,而不是在新一行的开始
  • scala推荐左括号在表达式结尾,而不是新一行的开始

块表达式和赋值

  • Scala的结果也是一个表达式,和其他语言不同,{}的最后一个表达式就是这个块表达式的值,因此可以使用块表达式初始化一个值,此特性和Erlang相同

    • val distance = { val dx=x0; val dy=y-y0; sqrt(dxdx+dydy**) }
  • Scala的负值动作是没有值的,严格来讲他的值是Unit

    • 比如 { r=r * n; n -= 1 } 的值是 Unit

    • 不要 x=y=1, 因为 y=1的值是()

输入和输出

  • print

  • println

  • printf

  • readInt

  • readDouble

  • readByte

  • readShort

  • readLong

  • readFloat

  • readBoolean

  • readChar

  • readLine

    • readLine(“Your name:”)

循环

  • while(n>0) {}

  • for(i < -1 to n) {}

    • 语法结构可以理解为: for ( i <- 表达式)

    • 1 to n 代表的是1.to(n), 即生成了1 到 n的数组

    • <- 代表将后面的表达式的值逐一负值到i,但是这里取决于表达式的类型,这里是一个Range类型

    • 如果期望表达式的范围是0到n-1 则可以使用 for( i <- until s.length) {}

    • for (ch <- “hello”)

      • 该表达式将字符循环负值给了ch
  • scala没有break和continue

    • 一. 使用Boolean型的控制变量

    • 二. 使用嵌套函数 — 你可以从函数中return

    • 三. 使用 Breaks 对象中的 break 方法

       import scala.util.control.Breaks._  
          
        breakable {  
          for (...) {  
              if (...) break; // 退出breakable块  
              ...  
          }  
        }
      
      • 这种实现其实是在 break 这个语句抛出了一个异常,然后 breakable 块捕获异常来实现的

      • 如果对性能要求很高的,应该尽量避免使用这套机制

高级for循环和for推导式

  • 通用表达式

    • for (变量1 <- 表达式1 [保护语句1]; [变量定义]…; 变量2 <- 表达式2 [保护语句2]) [yield 表达式]

      • 保护语句格式: if xxxx,

      • 是一个以if 开头的Boolean 表达式

    • for { 变量1 <- 表达式1 [保护语句1]
      [变量定义]
      ...
      变量2 <- 表达式2 [保护语句2] } [yield 表达式]

      • 可以使用圆括号,分号可以修改为换行
  • 例子

     for(i <- 1 to 3; j <- 1 to 3 if i != j ) print ( ... )
    

    等同于Go语言的

     for i := 0; i < 3; i++ {  
        for j := 0; j < 3; j++ {  
            if i != j {  
                fmt.Print(...)  
            }  
        }  
      }
    
    for (i <- 1 to 3; from = 4 - i; j <- from to 3) print(...)
    

    上面在for中定义了变量

    for (i <- 1 to 10) yield i % 3
    

    yield表达式,将生成结果:Vector(1,2,0,1,2,0,1,2,0,1)

     for { i <- 1 to 3  
        from = 4 - i  
        j <- from to 3 }
    

函数

  • Scala除了方法还支持函数,方法对对象进行操作,函数不是

  • def abs(x: Double) = if ( x >= 0) x else -x

    • 必须给出所有参数的类型

    • 如果函数比较复杂,则可以使用代码块{}, 将代码放到代码块里即可

    • 返回参数

      • 如果是非递归函数

        • 不需要给出返回类型,Scala编译器可以通过=符号右侧的表达式的类型推断出返回类型
      • 如果是递归函数

        def fac(n: Int): **Int** = if (n <=0) 1 else n * fac(n-1)
        

默认参数和带名参数

  • 定义

    • 我们在调用某些函数时并不显示的给出所有的参数值,对于这些函数我们可以使用默认参数

    • 传入的参数如果不带名字,则会按顺序对应到默认参数里

    • 有时候可以我们只想修改特定的默认参数,而这个参数又在其他的默认参数后面,因此我们需要使用带名参数的方法来传入值

  • 例子

    def decorate(str: String, left: String = "[", right: String = "]") = left + str + right
    

    left参数和right参数均有默认值,在调用的时候如果无特殊需求,则不需要传入值
    带名参数的调用方法

    def decorate("Hello", right = "]<<<")
    

变长参数

  • def sum(args: Int*) = {}

    • 调用方法

      • val s = sum(1, 4, 9, 16)

      • val s = sum(1, 4, 9, 16, 25, 100)

  • 可以传入任意数量的参数

  • 错误的使用方法

    • 例一

      • 错误

        • val s = sum(1 to 5)
      • 正确

        • val s = sum(1 to 5: _*)

          • 后面的 :_* 的作用类似Golang的

            • sum(numbers …)
  • 注意

    • 当你调用变长参数且参数类型为Object的Java方法时,你需要手工对基本类型进行转换

      • 例如

        • Java方法

          • MessageFormat public static String format(String pattern, Object... arguments)

            • 此处为Java的方法,且变参的类型为Object
        • 调用

          • var str = MessageFormat.format("The answer to {0} is {1}", "everything", 42.asInstanceOf[AnyRef])

          • 由于42是基本类型,因此需要手动转换到 Scala 的 AnyRef 上,AnyRef 等同于 Java 的 Object

过程

  • 定义

    • 如果函数体包含在花括号当中,但没有前面的 = 号, 那么返回类型就是Unit,这样的函数被称为 (procedure)

    • 过程不返回值,我们调用它仅仅是为了他的副作用

  • 例子

    • 我们只是想在屏幕上输出一段文字

      • def box(s: String) { … }

      • 大家仔细看,这里是没有等号的,没有写成 def box(s: String) = { … }

  • 习惯性问题

    • def box(s: String): Unit = { … }

    • 建议大家总是显式的声明Unit返回类型

懒值

  • 定义

    • 当val被声明为lazy时,他的初始化将被推迟,直到我们首次对他取值
  • 例子

    lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString
    

    当我们第一次访问的时候,这个文件的内容才会被读取,然后复制给 val

  • 对比说明

    val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString //在words被定义时即被取值
    lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString //在words被第一次使用时取值
    def words = scala.io.Source.fromFile("/usr/share/dict/words").mkString //words函数每次被调用时取值
    
  • 其他说明

    • lazy 特性并不是没有额外开销,我们每次访问都会有一个方法被调用,而这个方法将会以线程安全的方式检查该值是否被初始化过
    • 有点像Golang里的sync.Once

异常

  • 说明

    • 受检异常

      • scala中无java里的受检异常特性,即在函数定义的时候就要指出本函数将会抛出什么异常,如果代码里抛出了未在定义里事先列出的异常,则在编译时会报错

      • scala的设计者决定不支持此特性,因为在编译期间做彻底的检查也并不总是好事

  • 表达式

    • throw 表达式有特殊的类型 Nothing

      • 作用

        • if/else

          • if (x > 0) { sqrt (x)
            } else throw new IllegalArgumentException("x should not be negative")

            • 当 x > 0 时返回的值是Double

            • 当 x <=0 时抛出异常,既 Nothing 类型

            • 因此该 if/else 表达式的类型是 Double

  • 捕获异常

    • 语法

      • try { … } catch { … }

      • try { … } finally { … }

      • 作用和异常的抛出规则类似,如果finally 里出了异常,则会覆盖之前的异常

    • 例子

       try {  
          process(new URL("http://xxxx.com/yyy.gif"))  
        } catch {  
          case _: MalformedURLException => println("Bad URL:" + url)  
          case ex: IOException => ex.printStackTrace()  
        }
      
      • catch 采用的是模式匹配,此例子会将抛出的异常从上到下,依次对比异常类型,如果能匹配上则执行 => 后面的语句

      • 模式匹配部分类似Go的类型选择

       select v:=Exception.(type) {  
          case MalformedURLException:{  
              println("Bad URL:" + url)  
          }  
          case IOException:{  
              v.printStackTrace()  
          }  
        }
      

练习

  • 第一题

    def signum(v: Int): Int = {  
        if(v > 0 ){  
            1  
        } else if( v == 0 ){  
            0  
        } else {  
            -1  
        }  
      }
    
  • 第二题

    • 是 Unit,代表空
  • 第三题

    • var x=()

      • scala赋值动作本身是没有值的,即赋值动作的结果为Unit,因此 x=y=1, 他的第一步动作是 y=1, 得到 Unit 类型,如果x为Int类型则会报错,因此x应该为Unit类型
  • 第四题

    for( i<-1 to 10; j=10-i+1) System.out.println(j)
    
  • 第五题

    def countdown(n: Int):Unit = {  
        for( i<-1 to n; j=n-i+1)   
            System.out.println(j)  
      }
    
  • 第六题

    var value:Long = 1  
     for(char<- "Hello") value*=char  
     System.out.println(value)
    
    • 一开始做题时,书上的Hello值为 9415087488, 而我算出来是 825152896,后来发现如果 var value = 1 如果不写类型则是Int型,因此是存储不下的,要声明为Long才行

    • 此处比较坑爹的是,scala 的解释器,直接带入 72101108108111 后算出来的值也是错的, 还是erlang智能

      • scala> 72101108108111
        res2: Int = 825152896

      • Eshell V8.2 (abort with ^G)
        1> 72101108108111.
        9415087488

  • 第七题

    "Hello".foldLeft(1L)(_ * _.toInt)
    
  • 第八题

    def product(s: String):Long = {  
        s.foldLeft(1L)(_ * _.toInt)  
      }
    
  • 第九题

    def product(s: String,seq: Int = 0, value: Long = 1L):Long = {  
        if(seq>=s.length)  
            value  
        else  
            product(s, seq+1, value*s(seq))  
      }
    
  • 第十题

    def calc(x:Long, n:Long):Long = {  
        if(n>0){  
            if(n % 2 == 0L){  
                var y=calc(x,n/2)  
                y*y  
            } else {  
                x*calc(x,n-1)  
            }  
        } else if(n==0){  
            1  
        } else{  
            1/calc(x,-n)  
        }  
      }
    
《快学 Scala》 学习笔记 第二章.png

相关文章

网友评论

      本文标题:《快学 Scala》 学习笔记 - Chapter 2

      本文链接:https://www.haomeiwen.com/subject/oiwfbttx.html