scala方法和函数

Scala方法与函数详解

目录

方法的定义

默认参数

变长参数

过程

懒值(lazy)

方法VS函数

定义方式

表达形式

参数要求

方法和函数的相互转换


方法的定义

有以下几点需要注意:

  1. 所有参数必须制定类型;
  2. 递归方法,不能省略返回值类型,其他情况下可以忽略(方法可以通过=右侧的函数主体推断出返回值类型)
  3. 单行方法主体,可以与方法写在一行并忽略{}

默认参数

scala> def m1(name : String, left : String = "<", right : String = ">") = {
     | left + name + right
     | }
m1: (name: String, left: String, right: String)String

scala> m1("hello")
res0: String = <hello>

scala> m1("hello","<<",">>")
res1: String = <<hello>>

scala> m1("hello", right = ">>")
res2: String = <hello>>

scala> m1("hello", "<<")
res3: String = <<hello>

说明:

可以在定义参数的时候直接为参数赋值;

当调用方法时,可以不设置已有默认值的参数;

可以设置默认参数的值,设置值的时候会按照方法默认参数的顺序依次设置,也可以指定参数名对其进行赋值。

变长参数

当输入相同类型的参数个数无法确定时,可以使用变长参数,java和python中都有此特性

scala> def sum(args : Int*) = {
     |   var res = 0
     |   for(arg <- args) res += arg
     |   res
     | }
sum: (args: Int*)Int

scala> sum(1,2,3,4)
res0: Int = 10

sum方法的参数类型是Seq类型,如果有一个值的序列不能传入变长参数中,此时会报错

scala> sum(1 to 4)
<console>:13: error: type mismatch;
 found   : scala.collection.immutable.Range.Inclusive
 required: Int
       sum(1 to 4)
             ^

如果就是想传入一个已有序列,怎么办?scala设计之初肯定也是想过的,就是在出入的已有序列后追加“:_*”,表示将一个Range转成一个Seq

scala> sum(1 to 4 :_*)
res5: Int = 10

过程

在Scala中,定义方法时,如果方法体直接包裹在了花括号里面,而没有使用=连接,则方法的返回值类型就是Unit。这样的方法就被称之为过程。过程通常用于不需要返回值的方法。过程还有一种写法,就是将方法的返回值类型定义为Unit。其实过程就是相当于Java中无返回值的方法。

//将方法主体直接包含在{}中,并省略“=”,其实方法是一个返回Unit类型的方法
scala> def hello(name : String){ print("hello," + name) } 
hello: (name: String)Unit

scala> hello("liumingxin")
hello,liumingxin

即使我们在方法中依旧设置了返回值,过程返回值类型依旧是Unit

//即使设置了返回值,过程返回的依旧是Unit类型
scala> def hello(name : String){ print("hello," + name);"hello, " + name}
hello: (name: String)Unit

scala> hello("liumingxin")
hello,liumingxin

懒值(lazy)

在Scala中,提供了lazy值的特性,也就是说,如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算。这种特性对于特别耗时的计算操作特别有用。

lazy的调用并不做任何额外的开销,每一次访问lazy值的时候,都会调用一个方法,以线程安全的方式检查该值是否已经被初始化了。

scala> val a = 1 / 0
java.lang.ArithmeticException: / by zero
  ... 28 elided

scala> lazy val a = 1 / 0
a: Int = <lazy>

scala> a
java.lang.ArithmeticException: / by zero
  at .a$lzycompute(<console>:11)
  at .a(<console>:11)
  ... 28 elided

很明显,1/0是错误的,在不使用lazy的时候,创建变量直接报错;使用lazy之后,只有调用时才检查是否,如果创建了才进行调用。

在Spark中使用了大量的lazy。

方法VS函数

Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。

Scala 中的方法跟 Java 的类似,方法是组成类的一部分。

Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait(Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。) 的类的对象。

Scala 中使用 val 语句可以定义函数,def 语句定义方法。

定义方式

//方法的定义
scala> def m1(x: Int) = x + 1
m1: (x: Int)Int

//函数的定义
scala> val f1 = (x : Int) => x + 1
f1: Int => Int = $$Lambda$1036/961708482@1573e8a5

方法是一个以def开头的带有参数列表(可以无参数列表)的一个逻辑操作块,这正如object或者class中的成员方法一样。(上面有定义说明)

函数是一个赋值给一个变量(或者常量)的匿名方法(带或者不带参数列表),并且通过=>转换符号跟上逻辑代码块的一个表达式。=>转换符号后面的逻辑代码块的写法与method的body部分相同。

表达形式

scala> m1
<console>:13: error: missing argument list for method m1
Unapplied methods are only converted to functions when a function type is expect
ed.
You can make this conversion explicit by writing `m1 _` or `m1(_)` instead of `m
1`.
       m1
       ^

scala> f1
res1: Int => Int = $$Lambda$1036/961708482@1573e8a5

method(带参方法)不能作为最终的表达式(无参方法可以,但是这个就成了方法调用,因为scala允许无参方法调用时省略()括号)。

function可以作为最终的表达式出现。

方法名意味着方法调用,函数名只是代表函数自身。

参数要求

scala> def m2 = 100
m2: Int

scala> def m3() = 100
m3: ()Int

scala> val f2=() => 100
f2: () => Int = $$Lambda$1050/1002690318@78bce1c4

scala> val f3= => 100
<console>:1: error: illegal start of simple expression
       val f3= => 100
               ^

方法可以没有参数列表页可以是空的参数列表。

函数必须设置参数列表,可以是空的参数列表,但一定要设置。

方法和函数的相互转换

把 x => func(x) 简化为 func _ 或 func 的过程称为 eta-conversion(函数转换成方法)

把 func 或 func _ 展开为 x => func(x) 的过程为 eta-expansion(方法转换成函数)

可以在方法名后面加一个下划线强制变成函数,方法名与下划线之间至少有一个空格,没有会报错

eta-expansion过程:

//方法转变成函数 ETA-expansion,记得方法名与_之间加空格
scala> val f4 = m1
<console>:12: error: missing argument list for method m1
Unapplied methods are only converted to functions when a function type is expect
ed.
You can make this conversion explicit by writing `m1 _` or `m1(_)` instead of `m
1`.
       val f4 = m1
                ^

scala> val f4 = m1_
<console>:11: error: not found: value m1_
       val f4 = m1_
                ^

scala> val f4 = m1 _
f4: Int => Int = $$Lambda$1054/2011547493@5daa621b

  eta-conversion过程:

//ETA-conversion,也验证了m4是一个方法,并不是一个函数
scala> def m4(x : Int) = f1(x)
m4: (x: Int)Int

scala> m4
<console>:13: error: missing argument list for method m4
Unapplied methods are only converted to functions when a function type is expect
ed.
You can make this conversion explicit by writing `m4 _` or `m4(_)` instead of `m
4`.
       m4
       ^

 

<think>好的,用户想了解Scala函数方法的区别以及如何定义。首先,我需要回忆Scala的基础知识,确保信息准确。记得函数方法Scala中是不同的概念,但有时候容易混淆。 首先,方法的定义是使用def关键字,属于类、特质或对象的一部分,通常与面向对象编程相关。而函数是一个独立的代码块,可以赋值给变量,作为参数传递或作为返回值,是一等公民。这点需要明确区分。 接下来,应该从定义方式入手。比如,方法需要在类或对象中定义,而函数可以通过lambda表达式或FunctionN的实例来创建。例如,方法可能有参数列表返回类型,而函数可以作为变量存储。 然后,作用域也是一个区别点。方法在类或对象内部定义,而函数可以在任何地方定义,包括另一个函数内部。比如,用户提供的引用[2]中的例子,函数sayHello定义在main方法内部,这说明函数可以在方法内定义。 调用方式的不同也需要说明。方法调用依赖于对象实例,而函数可以直接调用或传递。此外,方法可以转换成函数,比如通过方法名加空格下划线的方式,如method _,这样可以将方法提升为函数。 关于定义示例,用户给的引用[2]中的代码展示了在对象内部定义方法函数的情况。比如,main方法中定义了函数sayHello,而方法可能定义在类或对象的结构体中。需要分别给出方法函数的定义例子,比如在对象中定义一个方法,以及在方法内部或外部定义一个函数。 可能还需要提到,函数可以作为参数传递给高阶函数,如map、filter等,而方法需要先转换成函数才能使用。这点对于理解函数式编程在Scala中的应用很重要。 用户的问题还可能涉及它们的使用场景,比如在函数式编程中更常用函数,而在面向对象中更常用方法。但Scala是混合范式,所以两者都可以灵活使用。 需要检查是否有遗漏的点,比如类型推断、参数列表的差异。例如,方法可以有多参数列表,而函数的参数必须在一个列表中。此外,方法可以有隐式参数,而函数可能不支持。 最后,确保回答结构清晰,分点说明区别,并提供定义示例。同时,根据用户提供的引用内容,正确引用相关部分,如引用[1]提到函数是一等公民,可以在任何地方定义,作为参数返回值,这点需要强调。而引用[2]中的代码示例展示了函数定义调用的具体方式,应该结合这些例子进行说明。 总结回答的结构:先解释方法函数的定义,然后分点列出区别,包括定义位置、语法、作用域、转换方式等,再给出各自的代码示例,最后总结两者的主要区别应用场景。同时,生成相关问题,帮助用户深入理解。</think>### Scala函数方法的区别及定义方式 #### 一、定义方式 1. **方法** 使用`def`关键字定义,属于类、特质或对象的成员,具有面向对象特性。 **示例**: ```scala class Calculator { def add(a: Int, b: Int): Int = a + b // 类中定义方法 } ``` 2. **函数** 通过`val`或`var`定义,是独立的值(类型为`FunctionN`),可赋值给变量或作为参数传递[^1]。 **示例**: ```scala val multiply = (a: Int, b: Int) => a * b // 函数赋值给变量 ``` #### 二、核心区别 | 特性 | 方法 | 函数 | |-------------|-------------------------------|-------------------------------| | **定义位置** | 类/对象内部 | 任何地方(包括函数内部)[^2] | | **语法** | `def`关键字 | `=>`箭头表达式 | | **独立性** | 依附于对象 | 一等公民,可独立存在[^1] | | **参数列表** | 支持多参数列表(柯里化) | 单参数列表 | | **转换方式** | 可通过`方法名 _`转为函数 | 无需转换直接使用 | #### 三、代码示例对比 1. **方法定义与调用** ```scala object MathUtils { def square(x: Int): Int = x * x // 对象中的方法 } MathUtils.square(5) // 输出25 ``` 2. **函数定义与传递** ```scala val greet: String => Unit = (name: String) => println(s"Hello, $name") List("Alice", "Bob").foreach(greet) // 函数作为参数传递 ``` 3. **方法函数** ```scala def methodToFunction(x: Int): Int = x * 2 val func = methodToFunction _ // 方法转为函数 func(3) // 输出6 ``` #### 四、核心总结 - **方法**是面向对象的基本单元,**函数**是函数式编程的核心。 - 函数可灵活用于高阶操作(如`map`、`filter`),方法需转换后才能使用。 - 方法支持更复杂的语法(如柯里化),函数更轻量级。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值