Scala Functions vs Methods(scala中的函数和方法)

被墙的文章,转过来一下。

Scala has both functions and methods. Most of the time we can ignore this distinction, but sometimes we have to deal with the fact that they are not quite the same thing. 


In my  Scala Syntax Primer  I mention that I use the terms method and function interchangeably in the discussion. This is a simplification. In many situations, you can ignore the difference between functions and methods and just think of them as the same thing, but occasionally you may run into a  situation  in which the difference matters. This is analogous to how most of us treat mass and weight. In our daily lives on the surface of planet Earth, we treat them as interchangeable units, with 1Kg being the same as 2.2 pounds. But they are not quite the same thing: when an astronaut walks on the surface of the moon, his mass (kilograms) has not changed but his weight (pounds) is only about  one sixth  of what it was on Earth. 

In contrast to Kg vs pounds, you are rather more likely to come across a situation in which the distinction between Scala functions and methods is important than you are to be walking on the surface of the moon. So when can you ignore the difference between functions and methods, and when do you need to pay attention to it? You can answer that question once you understand the difference. 

A Scala method, as in Java, is a part of a class. It has a name, a signature, optionally some annotations, and some bytecode. 

A function in Scala is a complete object. There are a series of traits in Scala to represent functions with various numbers of arguments:  Function0 Function1 , Function2 , etc. As an instance of a class that implements one of these traits, a function object has methods. One of these methods is the  apply  method, which contains the code that implements the body of the function. Scala has special "apply" syntax: if you write a symbol name followed by an argument list in parentheses (or just a pair of parentheses for an empty argument list), Scala converts that into a call to the  apply  method for the named object. When we create a variable whose value is a function object and we then reference that variable followed by parentheses, that gets converted into a call to the  apply  method of the function object. 

When we treat a method as a function, such as by assigning it to a variable, Scala actually creates a function object whose apply method calls the original method, and that is the object that gets assigned to the variable. Defining a function object and assigning it to an instance variable this way consumes more memory than just defining the functionally equivalent method because of the additional instance variable and the overhead of another object instance for the function. Thus you would not want every method to be a function; but functions give you a great deal of power that is not available with just methods, and in those situations that power is well worth the additional memory used. 

Let's look at some details of this mechanism. Create a file  test.scala  with this in it:
 
 
class test { def m1(x:Int) = x+3 val f1 = (x:Int) => x+3 }
Compile that file with scalac and list the resulting files. Scala creates two files:  test.class  and  test$$anonfun$1.class . That strange extra class file is the anonymous class for the function object that Scala created in response to the function expression assigned to  f1 . If you use more than one function expression in your  test  class, there will be more than one anonymous class file, even if you write the same function expression over again. 

If you run  javap  on the  test  class, you will see this:
 
 
Compiled from "test.scala" public class test extends java.lang.Object implements scala.ScalaObject{ public test(); public scala.Function1 f1(); public int m1(int); public int $tag() throws java.rmi.RemoteException; }
Running  javap  on the function class  test$$anonfun$1  yields this:
 
 
Compiled from "test.scala" public final class test$$anonfun$1 extends java.lang.Object implements scala.Function1,scala.ScalaObject{ public test$$anonfun$1(test); public final java.lang.Object apply(java.lang.Object); public final int apply(int); public int $tag() throws java.rmi.RemoteException; public scala.Function1 andThen(scala.Function1); public scala.Function1 compose(scala.Function1); public java.lang.String toString(); }
Because this class implements the  Function1  interface we know it is a function of one argument. You can see that it contains a handful of methods, including the apply  method. 

You can also define a function in terms of an existing method by referencing that method name followed by a space and an underscore. Modify  test.scala  to add another line that does this:
 
 
class test { def m1(x:Int) = x+3 val f1 = (x:Int) => x+3 val f2 = m1 _ }
The  m1 _  syntax tells Scala to treat  m1  as a function rather than taking the value generated by a call to that method. Alternatively, you can explicitly declare the type of  f2 , in which case you don't need to include the trailing underscore:
 
 
val f2 : (Int) => Int = m1
In general, if Scala expects a function type, you can pass it a method name and have it automatically converted to a function. For example, if you are calling a method that accepts a function as one of its parameters, you can supply as that argument a method of the appropriate signature without having to include the trailing underscore. 

Back to our test file, now when you compile  test.scala  there will be two anonymous class files, one for the  f1  class and one for the  f2  class. You can use either definition for  f2 , they generate identical class files. 

If you use the  -c  option to  javap  to get the code of the second anonymous class, you can see the call to the  m1  method of the  test  class in the  apply  method:
 
 
public final int apply(int); Code: 0: aload_0 1: getfield #17; //Field $outer:Ltest; 4: astore_2 5: aload_0 6: getfield #17; //Field $outer:Ltest; 9: iload_1 10: invokevirtual #51; //Method test.m1:(I)I 13: ireturn
Let's fire up the scala interpreter and see how this works. In the following examples, input text is shown in  bold  and output text in regular weight.
 
 
scala> def m1(x:Int) = x+3 m1: (Int)Int scala> val f1 = (x:Int) => x+3 f1: (Int) => Int = <function> scala> val f2 = m1 _ f2: (Int) => Int = <function> scala> m1(2) res0: Int = 5 scala> f1(2) res1: Int = 5 scala> f2(2) res2: Int = 5
Note the difference in the signatures between  m1  and  f1 : the signature  (Int)Int  is for a  method  that takes one  Int  argument and returns an  Int  value, whereas the signature  (Int) => Int  is for a  function  that takes one  Int  argument and returns an  Int  value. 

At this point we seem to have a method  m1  and two functions  f1  and  f2  that all do the same thing. But  f1  and  f2  are actually variables that contain an instance of a generated class that implements the  Function1  interface, and that object instance has methods that  m1  does not have.
 
 
scala> f1.toString res3: java.lang.String = <function> scala> (f1 andThen f2)(2) res4: Int = 8
Because  m1  is itself a method, unlike  f1  you can't call methods on it:
 
 
scala> m1.toString <console>:6: error: missing arguments for method m1 in object $iw; follow this method with `_' if you want to treat it as a partially applied function m1.toString ^
Note that each time you separately reference a method as a function Scala will create a separate object.
 
 
scala> val f3 = m1 _ f3: (Int) => Int = <function> scala> f2 == f3 res6: Boolean = false
Even though  f2  and  f3  both refer to  m1 , and both do the same thing, they are not considered equal by Scala because function objects inherit the default equality method that compares identity, and these are two different objects. If you want two function values to be equal, you must ensure that they refer to the same instance of function object:
 
 
scala> val f4 = f2 f4: (Int) => Int = <function> scala> f2 == f4 res7: Boolean = true
Here are a few other examples showing that the expression  m1 _  is in fact a function object:
 
 
scala> m1 _ res8: (Int) => Int = <function> scala> (m1 _).toString res9: java.lang.String = <function> scala> (m1 _).apply(3) res10: Int = 6
As of Scala version 2.8.0, the expression  (m1 _)(3)  will also return the same value (there is a  bug  in previous versions that causes this syntax to give a  type mismatch  error). 

There are some other differences between methods and functions. A method  can be  type-parameterized, but an anonymous function can not:
 
 
scala> def m2[T](x:T) = x.toString.substring(0,4) m2: [T](T)java.lang.String scala> m2("abcdefg") res11: java.lang.String = abcd scala> m2(1234567) res12: java.lang.String = 1234
However, if you are willing to define an explicit class for your function, then you can type-parameterize it similarly:
 
 
scala> class myfunc[T] extends Function1[T,String] { | def apply(x:T) = x.toString.substring(0,4) | } defined class myfunc scala> val f5 = new myfunc[String] f5: myfunc[String] = <function> scala> f5("abcdefg") res13: java.lang.String = abcd scala> val f6 = new myfunc[Int] f6: myfunc[Int] = <function> scala> f6(1234567) res14: java.lang.String = 1234
So go ahead and keep converting pounds to kilograms by dividing by 2.2 (unless you are an astronaut), but when you start mixing functions and methods in Scala, keep in mind that they are not quite the same thing.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Scala函数function)和方法(method)是有一些区别的。 1. 定义方式:函数是通过`val`关键字定义的值,而方法是通过`def`关键字定义的类成员。 2. 形式:函数是一个独立的值,可以直接赋值给变量,作为参数传递给其他函数,或者作为返回值返回。方法则是属于类或对象的成员,需要通过实例或者类名来调用。 3. 参数列表:函数的参数列表可以用括号包裹,也可以省略括号。方法的参数列表总是需要用括号包裹。 4. 调用方式:函数可以直接使用参数列表调用,而方法需要通过对象或者类名来调用。 5. 带有副作用:函数通常是纯函数(pure function),即没有副作用的函数方法可以有副作用,例如修改对象的状态或者打印输出等。 下面是一个示例来说明函数方法的区别: ```scala // 定义函数 val add: (Int, Int) => Int = (x, y) => x + y // 定义类,并定义一个方法 class MyClass { def multiply(x: Int, y: Int): Int = x * y } // 创建类的实例 val obj = new MyClass() // 调用函数 val result1 = add(3, 4) println(result1) // 输出: 7 // 调用方法 val result2 = obj.multiply(3, 4) println(result2) // 输出: 12 ``` 需要注意的是,Scala方法可以转换为函数,而函数不能直接转换为方法。可以使用方法引用(method reference)或者使用下划线(_)来将方法转换为函数。例如: ```scala val multiplyFunc: (Int, Int) => Int = obj.multiply val multiplyFunc2: (Int, Int) => Int = obj.multiply _ ``` 总的来说,函数方法Scala有些许的差异,但在实际使用,它们可以互相转换和配合使用,根据需要选择合适的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值