点击上方“大数据与人工智能”,“星标或置顶公众号”
第一时间获取好内容
本篇文章为大家带来Scala面试指南,本文会结合数据分析工程师在工作中会用到的知识点和网络上搜集的Scala常用考点,组成一份Scala精选题库,并附上详细的解答,力图为Scala面试者扫清知识盲点,提炼经典考题。
为什么要考察Scala?
1开发需要
2语言本身的魅力
Scala作为一门面向对象的函数式编程语言,如其官网宣称的:“Object-OrientedMeets Functional”,这一句当属对Scala最抽象的精准描述,它把近二十年间大行其道的面向对象编程与旧而有之的函数式编程有机结合起来,形成其独特的魔力,可以使你的代码更简洁高效易于理解。同样的需求,不同水平的Scala工程师写出来的代码会有很大不同,所以考察Scala代码能力就能大致看出其编程水平。
Scala精选题库
学过Scala的同学肯定都会吐槽Scala难学,它将面向对象和函数式编程这两种不同的软件工程范式结合起来,它还有一个复杂的类型系统,所以对于Scala的考察涉及到的知识点非常多。想要通过Scala的面试,除了平时在学习和工作中的总结以外,刷题是一个很好的办法,本文会结合数据分析工程师工作中需要掌握的知识点做一个筛选,最终挑选出如下的考题,主要分为问答题和手写题,仔细看看有没有你不知道的知识点?
问答题
语言特性类
1 scala语言有什 么特点?
Scala 是一种有趣的语言。它一方面吸收继承了多种语言中的优秀特性,一方面又没有抛弃 Java 这个强大的平台,它运行在 JVM 之上,轻松实现和丰富的 Java 类库互联互通。它既支持面向对象的编程方式,又支持函数式编程。它写出的程序像动态语言一样简洁,但事实上它却是严格意义上的静态语言。总结起来就是面向对象和函数式的完美结合。
2什么是函数式编程?有什么优点?
简单来说,"函数式编程"是一种编程范式(programming paradigm),也就是如何编写程序的方法论。它属于结构化编程的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。
优点 :
1)代码简洁,开发快速,大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快; 2)接近自然语言,易于理解,函数式编程的自由度很高,可以写出很接近自然语言的代码; 3)代码管理方便,一个函数只需要给相应的参数,最终返回一个值,它不依赖也不改变外界的状态,只为了完成相应的运算,所以一个函数可以看做一个独立的单元,这样就方便单元测试和组合。 4)易于并发操作,不修改变量也就不存在锁线程的问题,可以放心地用于并发编程。
变量相关
变量相关的知识点会涉及修饰符、类型、参数等概念,以及如何定义变量、关键字的区别等问题,是比较基础的知识点,主要还是在于多总结然后进行理解。
3var,val和def三个关键字之间的区别?
4Scala访问修饰符有哪些?
5Scala中Unit类型是什么?
6Scala类型系统中Nil,Null,None,Nothing四种类型的区别?
Nil 代表一个List空类型,等同List[Nothing],根据List的定义List[+A],所有Nil是所有List[T]的子类; Null 是所有AnyRef的子类,在Scala的类型系统中,AnyRef是Any的子类,同时Any子类的还有AnyVal, null是Null的唯一对象; None 是一个object,是Option的子类型,代表没有值; Nothing 是所有类型的子类,也是Null的子类。Nothing没有对象,但是可以用来定义类型。例如,如果一个方法抛出异常,则异常的返回值类型就是Nothing(虽然不会返回)。
7你知道vararg参数的用法吗?
表达式相关
从表达式开始涉及的知识点会越来越复杂,会涉及到匹配表达式、循环、正则,另外也要关注if……else、值绑定和通配符如何使用。
8说说你对匹配表达式/模式匹配的理解?什么是模式守卫?
9yield是如何工作的?
10如何使用正则匹配?
import scala.util.matching.Regex
val pattern = "Scala".r
val str = "Scala is Scalable and cool"
println(pattern findFirstIn str)
//执行结果:Some(Scala)
常用的匹配运算有:findFirstIn-找到首个匹配项,findAllIn-找到所有匹配项,replaceFirstIn-找到首个匹配项并进行替换,replaceAllIn-找到所有的匹配项并进行替换。
函数相关
函数在Scala中是一等公民,对这一块的考察应该是最多的,函数如何定义?什么是方法?偏函数、闭包、科里化等概念如何理解?高阶函数有哪些?什么是尾递归?什么是部分应用函数?除此之外也要关注嵌套函数、函数字面量、传名参数这些知识点。
11方法和函数的区别?
12什么是偏函数?
13什么是高阶函数?
14什么是尾递归?
正常的递归,每一次递归操作,需要保存信息到堆栈中,当递归步骤达到一定量的时候,就可能会导致内存溢出,而尾递归,就是为了解决这样的问题,在尾递归中所有的计算都是在递归之前调用,也就是说递归一次计算一次,编译器可以利用这个属性避免堆栈错误,尾递归的调用可以使信息不插入堆栈,从而优化尾递归。
15说说你对闭包的理解?
16你了解部分应用函数吗?
17什么是函数柯里化?
def mulNormal(x:Int,y:Int) = x * y //该函数每次执行需要两个参数
def mulOneAtTime(x:Int) = (y:Int) => x * y //该函数先接受一个参数生成另外一个接受单个参数的函数
这样的话,如果需要计算两个数的乘积的话只需要调用:mulOneAtTime(5)(4)
18call-by-value和call-by-name求值策略的区别?
集合相关
集合虽然种类有限,但是如果不注意区分还是很容易弄混,需要掌握不同集合的特点、使用场景、常用的集合函数、集合间的转换等。
19Scala中的常用的集合有哪些?
20怎么理解可变集合和不可变集合?
21集合之间可以转换吗?举例说明下
22如何实现Scala和Java集合的兼容性?
23谈谈你对Scala中数组的理解
24你知道迭代器吗?
25Option类型的使用场景?
26Option,Try和Either三者的区别?
面向对象类
对于面向对象的考察更多是概念,如对象、类、抽象类、单例对象、伴生对象、构造器、特质,如何继承?还需要关注重载、apply/unapply方法、包装语法。
28什么是伴生对象和伴生类?
29类的参数加和不加关键字(val和var)有区别吗?
30case class(样本类)是什么?
31abstract class(抽象类)和trait(特质)的区别?
32如何进行多重继承?
其他类
33谈谈scala中的隐式转换
34什么是隐式参数?
35如何处理异常?
Scala通过捕获异常,捕获后可以进行处理,或者抛出给上游程序,抛出异常的方法和 Java一样,使用 throw 关键字。如要要对一段代码的执行进行异常检测,使用try将这段代码包起来,在catch语句中进行异常的匹配,借用了模式匹配的思想catch语句中是一系列的case字句。需要注意的是与try……catch成对出现的还有finally语句-用于执行不管是正常处理还是有异常发生时都需要执行的步骤。
手写题
通过手写代码(纸质试卷或者上机编码)来考查应聘者的编码能力是很常用的做法,手写代码直接考查面试者平时的代码积累,大多数人平时写代码都是使用开发工具-例如IDEA,会有代码提示、自动补全等功能,加快了编码者的工作效率,但是同时也弱化了手写代码能力,如果是去面试还是要强化一下这方面的能力。以下试题大部分参考《快学Scala》课后习题,可以用来检验下自己的手写代码能力。
01
编写一个函数printDown(n:Int),使用模式匹配,打印从n到0的数字。
参考答案:
import scala.language.postfixOps//程序执行的时候需要导入的包
def printDown(n: Int) {
n match {
case n if n >= 0 => {
(0 to n reverse) foreach println
}
case n if n < 0 => {
n to 0 foreach println
}
}
}
02
编写一段程序, 从文件中读取单词, 用一个可变映射来清点每一个单次出现的频率,读取这些单词的操作可以使用java.util.Scanner:
val in = new java.util.Scanner(new java.io.File("myfile.txt:)), while(in.hasNext()) 处理 in.next() 最后, 打印出所有单次和它们出现的次数。分析:本题考查了函数的使用、读取文件、可变集合Map、迭代器、循环,统计单词的个数在很多编程题中都会出现。
参考答案:
import java.io.File
import java.util.{Scanner, StringTokenizer}
def wordCount(path: String): Scala.collection.mutable.Map[String, Int] = {
import Scala.collection.mutable.Map
val res = Map[String, Int]()
val in = new Scanner(new File(path))
while (in.hasNext()) {
val st = new StringTokenizer(in.next())
while (st.hasMoreTokens()) {
val key = st.nextToken()
res(key) = res.getOrElse(key, 0) + 1
}
}
res
}
03
编写一个Time类, 加入只读属性hours和minutes, 和一个检查某一时刻是否早于另一时刻的方法before(other: Time): Boolean。Time对象应该以new Time(hrs, min)方式构建,其中hrs小时数以军用时间格式呈现(介于0和23之间)。
分析:本题考查类的构建
参考答案:
def timeDemo(hr: Int, min: Int): Unit = {
class Time(hr: Int, min: Int) {
val hour = hr
val minute = min
def before(t: Time): Boolean = {
if (hour < t.hour) true else if (hour > t.hour) false else if (minute < t.minute) true else false
}
def <(t: Time): Boolean = before(t)
}
println(new Time(1, 2) < new Time(2, 1))
}
04
编写一个函数,接受一个字符串的集合,以及一个字符串到整型值的映射,返回整型集合, 其值为能和集合中某个字符串相对应的映射的值。举例来说,给定Array("Tom","Fred","Harry")和Map("Tom"->3,"Dick"->4,"Harry"->5),返回Array(3,5), 提示: 用flatMap将get返回的Option值组合在一起。
参考答案:
def highOrderFunction(arr: Array[String], map: Map[String, Int]): Array[Int] = {
arr.flatMap (map.get _ )
}
highOrderFunction(Array("a", "b"), Map("a" -> 100, "b" -> 2)) foreach println
05
实现冒泡排序算法
1)比较相邻的元素,如果第一个比第二个大,就交换; 2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对; 3)针对所有的元素重复以上的步骤,除了最后一个; 4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较;
参考答案:
// 内层循环做排序
def bSort(data: Int, dataSet: List[Int]): List[Int] = dataSet match {
case List() => List(data)
case head :: tail => if (data <= head) data :: dataSet else head :: bSort(data, tail)
}
// 外层循环做拆分
def bubbleSort(l: List[Int]): List[Int] = l match {
case List() => List()
case head :: tail => bSort(head, bubbleSort(tail))
}
val list = List(97, 12, 43, 33, 7, 1, 2, 20)
println(bubbleSort(list))
//执行结果:List(1, 2, 7, 12, 20, 33, 43, 97
小结
本篇Scala面试指南通过精选题库的方式将数据分析工作中涉及到的知识点尽可能完整地分布到问题中,涉及到的Scala知识点有基础的也有概念比较绕的,在手写代码类中的题目难易结合,希望这篇文章能够帮助准备面试大数据分析相关岗位的数据从业者查漏补缺,完善自己的Scala知识库。
参考文献
[1] Scala学习手册,作者:Jason Swartz
[2] 快学Scala,作者:Cay S. Horstmann
[3] 【译】Scala面试问题(Scala interview questions),作者:IIGEOywq - https://www.jianshu.com/p/ace2bb24dc11
[4] scala面试题总结,作者:郭小白 - https://www.cnblogs.com/Gxiaobai/p/10460336.html
-end-