作业评讲 | 学习笔记

简介: 快速学习作业评讲

开发者学堂课程【Scala 核心编程 - 进阶作业评讲学习笔记,与课程紧密连接,让用户快速学习知识。

课程地址https://developer.aliyun.com/learning/course/610/detail/9128


作业评讲


内容介绍:

一、模式匹配

二、高阶函数


一、模式匹配

1.利用模式匹配,编写一个 swap 函数,接受一个整数的对偶,(也就是说,对偶里的元素都是 int 类型),返回对偶的两个组成部件互换位置的新对偶。

(1)思路

先进行匹配,然后进行case操作,最后返回。

(2)过程

def swap[S, T](tup: (S, T)) = {

tup match {

case(a,b)=> (b,a)

}

}

该题难度较小,但要注意的是在写对偶时,对偶的类型的写法如果对偶的元素值类型都是 int,可以将 def swap[S, T](tup: (S, T)) = {

改写为 def swap[S, T](tup: (Int, Int)) = { 。原来的代码更为灵活一些。

(3)代码演示

在chapter16中点击“New”“Package”新建一个包,命名为homework,再在该包中新建一个文件“Exercise01”,并设置为object。

编写函数:

package com.atguigu.chapter16.homework

object Exercise01

def main (args:Array[string]): Unit = {

}

//需求:利用模式匹配,编写一个 swap 么函数,接受一个整数的对偶,返回对偶的两个组成部件互换位置的新对偶。

def swap[T, S](tup: (T, S)): Unit = {

//此处 swap的泛型顺序可以随意写入,且函数最终要返回值,返回值的类型可以设定,但是也可以忽略,用类型推导式即可,即改为def swap[T, S](tup: (T, S)) = {。

tup match {

case(a,b)=> (b,a)

//匹配到a,b两个变量是自行写入的变量,case中的变量匹配,会默认将tup里的第一个值传给a,第二个值传给b,然后直接将其交换位置即可。

case _ => println("没有匹配到")

}

}

(4)代码测试

在搭建好函数之后,可以在前面的 object 部分输入具体的值,进行试验,如“println (swap((10,20))”,若返回值为(20,10),则正确。运行代码,返回的结果为(20,10)。

使用模式匹配可以非常轻松地实现两个数的交换,甚至是任意数的位置互换。

2.利用模式匹配,编写一个 swap 函数,交换数组中的前两个元素的位置,前提条件是数组长度要至少为2。

(1)代码演示

package com.atguigu.chapter16.homework

object Exercise02

def main (args:Array[string]): Unit = {

}

//需求:利用模式匹配,编写一个swap函数,交换数组中的前两个元素的位置,前提条件是数组长度要至少为2。

def swap(arr:Array[Any]) = {

//题目中要匹配两个数组,那可以通过直接接收数组解决。而此处并没与指定数组的类型,可以默认为是any。同样可以使用类型推导,省略“: Unit”。

array match {

case Array(first,second,rest @_*) =>Array(second,first) ++ rest

//之前有学习过数组匹配的方法,如果数组有两个值为first、second,后续的匹配可以用rest @_*。

题目中要求前两个位置变化,即此时定义的数组至少有两个值,返回时,直接将前面的两个值second和first进行交换,后面的值不动即可。

case _ => arr

//若没有匹配到(数组长度不大于2,即不满足交换的条件),就按照原来的值返回

}

}

(2)代码测试

在搭建好函数之后,可以在前面的object部分中任意编写一个数组(数组类型不做要求),进行试验。

如“println(swap(Array(1,2,"hello"))) ”,若返回值为(2,1,"hello"),则代码正确。但按照目前的代码输出结果无法显示,则可以将“println(swap(Array(1,2,"hello"))) ”改为“println(swap(Array(1,2,"hello")).toBuffer) ”,最终结果显示为(2,1,"hello")。

若数组长度不够,如“println(swap(Array("tom"))) ”,数组中只有tom,也就不存在前两个值的交换,就会将原来的值直接返回返回值为"tom"。

3.编写一个函数,计算List[Option[Int]]中所有非None值之和,不得使用match语句。

(1)思路分析

List 中的值为 Option,Option的值类型为Int,不得使用match语句来计算其中非None值之和。我们之前学过getOrElse,这个语句可以解决该问题。

具体过程为:

通过遍历之后,通过_.getOrElse操作,如该值为None,则输出值为0,若该值不是None,则输出该值,因此通过该操作,输出值为通过_.getOrElse(0)+…。

(2)代码演示

在 homework 中新建一个文件“Exercise03”,并设置为object。编写函数:

package com.atguigu.chapter16.homework

object Exercise03

def main (args:Array[string]): Unit = {

}

//需求:返回非None的和

def  main(args:Array[String]):Unit={

val lists=List(Some(1),Some(3),None,Some(10),None)

println(mysum(lists))  //14

}

def mysum(lst: List[Option[Int]])=Ist.map(_·getOrElse(0)).

sum

//x的值中包含 List,最后接收了 List(option)之后遍历

//理解Ist.map(_·getOrElse(0)).sum:

若此处输入Ist.map(),系统提示Ist.map()中传入的值是Option[Int],即严格来写应为Ist.map((x:Option[Int]) =>x._getOrElse(0)).sum(其中x:Option[Int]是传入的形参,而“=>”右侧则是函数的函数体,即如果能得到即本身不是None,则返回本身的值,如果不能得到即本身是None,则返回0。在返回之后,对返回值求和)

//结合上方的“val lists=List(Some(1),Some(3),None,Some(10),None)”,当将lists传给mysum之后,即会使用getOrElse()语句遍历List中的各个值,若该值为1,则输出1,若为3,则输出3,若为None,则输出0,最终输出的集合应为1  3  0   10   0,则最终经过sum之后结果为14。

但是这种写法有些冗余,可以对之进行简化。因为该条代码中可以进行类型推导,因此“Ist.map((x:Option[Int]) ”中的“:Option[Int]”可以省略,即直接简化为“Ist.map((x) ) =>x._getOrElse(0)).sum”;此外对于x,在整条代码中相当于只出现过一次,因此,“(x) ) =>x”亦可省略,即简化为“Ist.map(._getOrElse(0)).sum”

}


二、高阶函数

1、编写一个 compose 函数,将两个类型为Double=>Option Double的函数组合在一起,产生另一个同样类型的函数。

如果其中一个函数返回None,则组合函数也应返回None。例如:

def f(x:Double)=if(x>0)Some(sqrt(x))else None

def g(x:Double)=if(x!=1)Some(1/(x-1))else None

val h=compose(f.g)

h(2)将得到Some(1),而h(1)和h(0)将得到None

(1)思路分析

本题的主要难度在于审题,compose函数即合并函数,合并的目的在于将两个函数组合在一起,且这两个函数都是接收Double,并返回Option[Double]的函数,即将这两个函数整合在一起产生另外一个同样类型的新的函数。

对于新的函数来说,如果整合之前的两个函数里其中一个函数为None,则新函数返回None。

下面一段代码的含义是:

已知两个函数关于x的函数f(x)和g(x)。其中第一条代码def f(x:Double)=if(x>0)Some(sqrt(x))else None的含义是f(x)函数可以接受Double,“if(x>0)Some(sqrt(x))else None”为其函数体,意义是如果x大于0,就返回sqrt(x),否则就返回None;第二条代码def g(x:Double)=if(x!=1)Some(1/(x-1))else None的含义是g(x)函数可以接受Double,if(x!=1)Some(1/(x-1))else None为其函数体,意义是如果x不等于1,则返回1/(x-1),否则(x=1)返回None。最后val h=compose(f.g)给出了我们需要完善的compose函数的格式,它是一个高阶函数。

最后题目中又给出了一个案例,如果传入“2”,则返回值为Some(1);若传入“1”或“0”,则返回值为None。

总之,本题的最终目的在于将两个给出的函数合并在一起,返回h,h在两个函数中通用。

(2)代码演示

在 homework 中新建一个文件“Exercise04”,并设置为 object。编写函数:

def main(args: Array[String]):Unit={

//引入所需的包

import scala.math.sqrt

val h=compose(f,g)

println(h(2))

}

//给出的两个函数

def f(x:Double)=if(x>0)Some(sqrt(x))else None

def g(x:Doable)=if(x!=1)Some(1/(x-1))else None

//根据需求合并为一个函数,形式为compose(f.g)

//案例:h(2)将得到Some(1),而h(1)和h(0)将得到None,即在合并的函数中传入2,输出Some(1)。再具体分析,“2”应该是g(x)函数输出的值,因为要想合并函数输出值不是None,则两个函数的输出值都不能是None。

若2是由f(x)得出,则返回值为sqrt(x),则返回值与题目不符,因此h(2)将得到Some(1)是通过g(x)函数得出的。

def compose(f:Double=>Option[Double],g:Double=>Option[

Double])={

//f:Double=>Option[Double]与g:Double=>Option[Double]

是形参的格式

(x:Double)=>

//返回了一个匿名函数,x:Double 为匿名函数的形参,因为返值

也是 Double;

//合并

//1.只要有一个函数返回None,组合函数也返回None

if(f(x) == None| g(x) == None) None

//假如f(x)返回None,或者g(x)返回None,则组合函数返回None

//2.如果新函数传入的值为2,则说明该函数是由g(x)函数得出的

3.代码测试

此处进行代码的传递即可。

val h = compose(f,g)

println(h(2))  //1

println(h(1) +""+h(0))  //None和None

运行代码一下,返回值为1.0    None   None。若之前的函数中输入为else f(x),则输出结果为1.414…,则不符合案例提示。

2.编写函数values(fun:(Int)=>Int,low:Int,high:Int),该函数输出一个集合,对应给定区间内给定函数的输入和输出。

比如,values(x=>x*x,-5.5)应该产出一个对偶的集合(-5,25),(-4,16),(-3,9),…,(5,25)

(1)思路分析

values(fun:(Int)=>Int,low:Int,high:Int)  //给出了函数形式

该高阶函数的作用是输出一个集合,这个集合可以对应给定区间内按照给定函数的输入和输出,即最小值和最大值相当于一个low-to-high的区间。

即通过fun函数对值进行处理,会得出一个对偶,该对偶的值为(low,fun(x)),且处理一个值即会返回一个值,最终形成一个集合。也就是最终的values会遍历low-to-high区间,map该fun函数,且该fun函数会返回一个对偶,最终进行组合。

(2)代码演示

在homework中新建一个文件“Exercise05”,并设置为object。

package com.atguigu.chapter16.homework

object Exercise05 {

def main(args:Array[String]):Unit={

}

//需求:

编写函数values(fun:(Int)=>Int,low:Int,high:Int),该函数输出一个集合,对应给定区间内给定函数的输入和输出。比如,values(x=>x*x,-5.5)应该产出一个对偶的集合(-5,25),(-4,16),(-3,9),…,(5,25)

def values(fun:(Int)=>Int,low:Int,high:nt)={

//函数的形式

var newlist=List[(Int,Int)]()

//由于最终返回值是一个集合,因此定义了一个空的List,准备放入对偶

low to high foreach  {

//进行遍历low to high

//以下为要处理的函数

item = >      // item为遍历得到的值

newlist = ( item,fun( item)) : :newlist

//将遍历得到的值交给函数fun( item)处理,然后进行组合拼接

}

newlist  //返回

}

(3)代码测试

//函数形式x=> x*x,因此在调用时按照该式子调用即可

println (values(x:Int)  = >x*x,-5,5))

//而 newlist = ( item,fun( item)) : :newlist原先为空的集合,经过第一次处理结果为: newlist = (-5,25) : :List() =>(-5,25);

经过第二次处理结果为: newlist = (-4,16) : :List((-5,25)) => (-5,25),(-4,16);(注:新的结果会加在前方)

……

运行代码,最终的输出结果为(5,25),(4,16),(3,9),…,(-4,16),(-5,25)发现此时输出的结果顺序不一致。为得出与案例中一致的顺序,可以改变传入的值的顺序,即

println (values(x:Int)  = >x*x,-5,5,reverse))

再次运行代码,结果为(-5,25),(-4,16),(-3,9),…,(5,25)。

3、如何用 reduceLeft 得到数组Array(1,333,4,6,4,4,9,32,6,9,0,2)中的最大元素?

object Exerciseo06 {

def main(args: Array[String]):Unit={

val arr = Array(1,333,4,6,4,4,9,32,6,9,0,2)

//改写下,

print(arr.reduceLeft((l,r)=>if(I>=r)1 else r))

}

def  f1(l:Int,r:Int): Int = {

// f1接收两个值l与r

if(l>r) l     else r

//如果l>r,则返回l;否则返回r。即返回较大的值。

}

}

4、用to和reduceLeft实现阶乘函数,不得使用循环或递归

def factorial(n:Int): Int= {

1 to  n reduceLeft(_*_)

}

(1)代码演示:

在homework中新建一个文件“Exercise06”,并设置为object。

package com.atguigu.chapter16.homework

object Exercise06 {

def main(args:Array[String]):Unit={

}

def factorial(n:Int): Int= {

1 to  n reduceLeft(_*_)

//以上为简写,全写应为1 to  n reduceLeft((x:Int,y:Int)  => x*y,因为reduce为二元函数,因此应接收两个值。由于可以进行类型推导,且x与y分别均只出现过一次,因而可以省略。

}

}

(2)代码测试

println(factorial(3))  //  1*2*3=6

运行代码,结果为6,说明代码无误。

4.编写函数 largest(fun:(Int)=>Int,inputs:Seq[Int]),输出在给定输入序列中给定函数的最大值。

举例来说,largest(x=>10*x-x*x,1 to10)应该返回25,不得使用循环或递归。

(1)思路分析

本题的难点在于审题接收函数。题目中表明新函数接收的值类型是Int,返回的值类型也是Int。

但要求的是input集合里面的最大值。即输出在给定输入序列中给定的函数的最大值,也就是说,要将imputs中的元素分别传给fun函数,从统计结果中输出最大值。

(2)代码演示

在 homework 中新建一个文件“Exercise07”,并设置为object。

package com.atguigu.chapter16.homework

object Exercise07 {

def main(args:Array[String]):Unit={

}

//需求:

①编写函数 largest(fun:(Int)=>Int,inputs:Seq[Int]),

//largest是一个高级函数,它可以接收函数fun:(Int)=>Int,及一个序列inputs:Seq[Int]

②输出在给定输入序列(inputs)中给定函数(fun:(Int)=>Int)的最大值。即将inputs中的每个元素传递给fun函数中进行计算,返回最大值

③如传入的函数是largest(x=>10*x-x*x,1 to10),其中x是该函数的形参,函数体是10*x-x*x,x=>10*x-x*x表示接收到的匿名函数,1 to10为imputs,应返回值为25

④不得使用循环或递归,即要求使用高阶函数来完成

实际上就是遍历,map求最大值。

A.课堂版(较易理解)

def largest(fun:(Int)=>Int,inputs:Seq[Int]) = {

inputs.map((n:Int)  => fun(n)).max

//inputs.map((n:Int)  => fun(n))的结果为将1到10依次传入fun(n)中得到的值的集合,求这个集合的最大值即可解决这个问题

//同时,n在该代码中也相当于只出现了一次,可省略为inputs.map( fun(_)).max

}

}

(3)代码测试

val maxval =largest(x=>10*x-x*x,1 to 10)

println("maxval"+maxval)

运行代码,最后结果是maxval=25。

课件版

def largest1(fun:(Int)=>Int,inputs:Seq[Int])= inputs.foldLeft(1)

((a,b)=> if(fun(b)>a) fun(b) else a)

deflargest2(fun:(Int)=>Int,inputs:Seq[Int])=inputs.map(fun(_)

).max

println(largest1(x=>10* x-x*x,1 to 10))

println(largest2(x=>10* x-x*x,1to10))

5.要得到一个序列的对偶很容易,比如:

val pairs =(1 to 10) zip (11 to 20)

编写函数adjustToPair,该函数接受一个类型为(Int,Int)=>Int的函数作为参数,并返回一个等效的,可以以对偶作为参数的函数。举例来说就是:adjustToPair(_*_)((6,7))应得到42,然后用这个函数通过map计算出各个对偶的元素之和

def ajustToPair(fun:(Int,Int)=>Int)=(x:(Int,Int))=>fun(x._1,x._2) val x=ajustToPair(_*_)((6, 7))

println(x)

val pairs=(1 to 10) zip (11 to 20)

println(pairs)

val y=pairs.map(ajustToPair(_+_))

println(y)

6.实现一个 unless 控制抽象,工作机制类似if,但条件是反过来的。

def unless(condition:=>Boolean)(block:=>Unit){if(!condition){b

lock}}

unless (0>1){ println("Unless!") }

unless我们学习过,在控制抽象里,学习过实现well循环,写法也完全相同。

“=>Boolean)(block:=>Unit”就是传入一个代码块,0>1传了一个代码块,println("Unless!")就是一个代码块,不再详细讲述,可以回顾控制抽像。

相关文章
|
1月前
|
JavaScript 搜索推荐
1+x作业0609
1+x作业0609
36 2
|
8月前
|
分布式计算 资源调度 监控
没有监控的流处理作业与茫茫大海中的裸泳无异 - 附 flink 与 spark 作业监控脚本实现
没有监控的流处理作业与茫茫大海中的裸泳无异 - 附 flink 与 spark 作业监控脚本实现
|
25天前
|
存储 监控 数据处理
Flink⼤状态作业调优实践指南:Datastream 作业篇
本文整理自俞航翔、陈婧敏、黄鹏程老师所撰写的大状态作业调优实践指南。
56249 5
Flink⼤状态作业调优实践指南:Datastream 作业篇
|
10月前
|
机器学习/深度学习 数据可视化 大数据
智能控制大作业
智能控制大作业
Day01-作业
猜测黑姑娘的年龄
45 0
|
开发者 Python
作业讲解2|学习笔记
快速学习作业讲解2
134 0
作业讲解2|学习笔记
|
前端开发
前端3.25作业
前端3.25作业
60 0
|
Scala 开发者
作业评讲|学习笔记
快速学习作业评讲。
95 0
|
JSON 数据格式 开发者
作业讲解|学习笔记
快速学习作业讲解
92 0
|
开发者 Python
作业评比|学习笔记
快速学习作业评比
68 0