文章目录
- 一、第1关:方法
- [S 三角形 ](#S 三角形)
- 二、第2关:Scala函数以及函数调用
一、第1关:方法
任务描述
本关任务:根据三角形的三边长 a、b、c,返回三角形的面积。
任意三角形面积公式-海伦公式
已知三角形三边a、b、c,则
S
三角形
p(p−a)(p−b)(p−c)
p=
2
1
(a+b+c)
相关知识
为了完成本关任务,你需要掌握:
什么是方法;
方法的使用。
方法
定义函数最常用的方式是作为某个对象的成员。这种函数被称为方法(method)。换句话来说在类中定义的函数即是方法。
定义方法的基本格式是:
def 方法名称 (参数列表):返回值类型 = 方法体
如果你不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型也将是一个抽象类型。
方法定义由一个 def 关键字开始,接着为该方法的名称,紧接着是可选的参数列表(意味着可写可不写),一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体。
接着我们来看几个定义方法的示例:
参数列表为空,返回类型为 Unit 的方法:
def f1() : Unit = {
println("f1")
}
返回 Unit 类型的函数,但是没有显式指定返回类型。(当然也可以返回非 Unit 类型的值):
def f2() = {
println("f2")
}
如果参数列表有多个时,可以使用逗号连接,当用 return 进行返回时,方法中必须明确指定返回值类型:
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
返回值类型有多种可能,此时也可以省略 Unit:
def f3(content: String) = {
if(content.length >= 3)
content + "喵喵喵~"
else
3
递归方法必须明确指定方法返回值类型:
def factorial(n: Int): Int = {
if(n <= 0)
1
else
n * factorial(n - 1)
}
方法的最简形式:
def m = 100
方法的调用
Scala 提供了多种不同的方法调用方式:
直接通过方法名(参数列表)调用:
functionName( 参数列表 )
如果方法使用了实例的对象来调用,我们可以使用类似 Java 的格式 (使用 . 号):
instance.\]functionName( 参数列表 ) 这里我们主要介绍第一种: 示例: object Test { def main(args: Array\[String\]) { println( "Returned Value : " + addInt(5,7) ); } def addInt( a:Int, b:Int ) : Int = { var sum:Int = 0 sum = a + b return sum } } 执行结果: Returned Value : 12 注意:当方法没有参数列表或参数列表为空时,调用该方法时可以省略方法后的括号。 编程要求 仔细阅读右侧编辑区内给出的代码框架及注释,在 Begin-End 间编写程序代码,定义一个 area 方法,该方法具有三个参数,类型都是 Int 型,分别代表三角形的三条边长,返回值类型为 Double;要求根据三角形的三边长,返回三角形的面积。 测试说明 平台将使用测试集运行你编写的程序代码,若全部的运行结果正确,则通关。 例: 测试输入: 3 4 5 预期输出: 三角形的面积为:6.00 ### 实验代码: ```python import scala.io.StdIn object Triangle { /********** Begin *********/ def area(a: Int, b: Int, c: Int): Double = { val s = (a + b + c) / 2.0 math.sqrt(s * (s - a) * (s - b) * (s - c)) } /********** End *********/ def main(args: Array[String]): Unit = { val a: Int = StdIn.readInt() val b: Int = StdIn.readInt() val c: Int = StdIn.readInt() printf("三角形的面积为:%.2f", area(a, b, c)) } } ```  ## 二、第2关:Scala函数以及函数调用 任务描述 本关任务:计算小明请完 5 顿饭之后还剩多少钱。 相关知识 为了完成本关任务,你需要掌握: 什么是函数; 如何调用函数。 函数定义 Scala 作为支持函数式编程的语言,Scala 函数式编程是 Scala 的重中之重,高级函数也是其独特的一个特性。首先,函数/变量同是一等公民,函数与变量同等地位,函数的定义可以单独定义,可以不依赖于类、接口或者 object,可以独立存在、独立使用,并且可以赋值给变量。 我们上一关所学习的方法就是函数的一种形式。接下来我们来看函数的定义: (函数的参数列表)=\>{函数体内容} 由于该函数需要被别人调用,所以要用一个变量来引用。 val 变量名称 =(函数的参数列表)=\>{函数体内容} 示例: // 将 x 的值加 1 val increase = (x:Int) =\> x+1 =\> 表示该函数将左侧的内容(任何整数 x)转换成右侧的内容(x+1)。 因此,这是一个将任何整数 x 映射成 x+1 的函数。 复杂的定义形式: val 变量名称:(输入参数类型) =\> 返回值类型 =(输入参数的引用)=\>{函数体内容} 示例: // 计算 x 和 y 的和 val add: (Int, Int) =\> Int = (x:Int, y:Int)=\> x+y 如果你想要在函数中包含多于 1 条语句,可以将函数体用花括号括起来,每条语句占一行,组成一个代码块(block)。跟方法一样,当函数值被调用时,所有的语句都会被执行,并且该函数的返回值就是对最后一个表达式求值的结果。 参数列表对于方法是可选的,但是对于函数是强制的。 方法可以没有参数列表,参数列表也可以为空。但是函数必须有参数列表(参数列表可以为空)。 而函数可以直接赋值给变量,可以让函数很方便的传递。 函数调用 传值调用和传名调用 Scala 的解释器在解析函数参数(function arguments)时有两种方式: 传值调用(call-by-value):先计算参数表达式的值(reduce the arguments),再应用到函数内部; 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部。 object Test { def main(args: Array\[String\]): Unit = { callByValue(something()) println("------------------------------") callByName(something()) } def something() = { println("calling something") } def callByValue(x: Int) = { println("x1=" + x) println("x2=" + x) } def callByName(x: =\> Int) = { println("x1=" + x) println("x2=" + x) } } 执行结果: calling something x1=1 x2=1 *** ** * ** *** calling something x1=1 calling something x2=1 从执行结果上来看,我们可以得出: 在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。 这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。 指定函数参数名调用 一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数。 object Test { def main(args: Array\[String\]) { printInt(7,5); // 默认按参数顺序调用 println("------------------------------") printInt(b=5, a=7); // 指定函数参数名调用 } def printInt( a:Int, b:Int ) = { println("Value of a : " + a ); println("Value of b : " + b ); } } 执行结果: Value of a : 7 Value of b : 5 *** ** * ** *** Value of a : 7 Value of b : 5 注意:当参数列表为空时,直接写方法名可以调用方法,而函数名只是代表函数对象本身。因为保存函数字面量的变量(又称为函数名或者函数值)本身就是实现了 FunctionN 特质的类的对象,要调用对象的 apply 方法,就需要使用obj()的语法。所以函数名后面加括号才是调用函数。如下: 如果我们想要直接操纵方法的话,可以使用下划线(_)将方法转换为函数。 object TestMap { def ttt(f:Int =\> Int):Unit = { val r = f(10) println® } val f0 = (x : Int) =\> x \* x //定义了一个方法 def m0(x:Int) : Int = { //传递进来的参数乘以10 x \* 10 } //将方法转换成函数,利用了神奇的下滑线 val f1 = m0 _ def main(args: Array\[String\]): Unit = { ttt(f0) //通过m0 _将方法转化成函数 ttt(m0 _) // 如果直接传递的是方法名称,scala相当于是把方法转成了函数 ttt(m0) } } 编程要求 仔细阅读右侧编辑区内给出的代码框架及注释,在 Begin-End 间编写程序代码,计算小明请完 5 顿饭之后还剩多少钱。具体要求如下: 变量 money 定义了小明的钱数,初始值为 1000 元; eat 方法中定义了每请一顿饭消耗多少元; balance 方法实现计算小明还剩多少元的功能; 定义一个 printMoneyByValue 方法,该方法传名调用 balance 函数,类型为 Int 的参数,打印连续五次调用所给参数后,小明还剩多少元; 定义一个 printMoneyByName 方法,该方法传值调用 balance 函数,类型为 Int 的参数,打印连续五次调用所给参数后,小明还剩多少元; 输出格式:"你的余额现在为:xx",xx 为剩余钱数。 测试说明 平台将使用测试集运行你编写的程序代码,若全部的运行结果正确,则通关。 例: 测试输入:无 预期输出: 你的余额现在为:950 *** ** * ** *** 你的余额现在为:650 ### 实验代码: ```python object Test { var money = 1000 // 吃一顿消费50 def eat(): Unit = { money = money - 50; } // 余额 def balance(): Int = { eat() money } /********** Begin *********/ def printMoneyByValue(balanceFunc: => Int): Unit = { val currentMoney = balanceFunc println(s"你的余额现在为:$currentMoney") } def printMoneyByName(mealCost: Int): Unit = { for (_ <- 1 to 5) { eat() } val remainingMoney = money println(s"你的余额现在为:$remainingMoney") } /********** End *********/ def main(args: Array[String]): Unit = { printMoneyByValue(balance()) println("-------------------") printMoneyByName(balance()) } } ```  *** ** * ** ***