这些scala方法中的下划线使用之间的区别 [英] The differences between underscore usage in these scala's methods

查看:103
本文介绍了这些scala方法中的下划线使用之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这些代码的下划线使用与下面的术语名称有什么区别:(请参阅处理程序(资源)部分)

1。

  def readFile [T](f:File)(处理器:FileInputStream => Byte => T):T = {
val resource = new java.io.FileInputStream(f)
try {
val hh = handler(resource)_
hh(2)
} finally {
resource.close()
}
}

val bs = new Array [Byte] (4)

readFile(new File(scala.txt)){
input => b:字节=> println(Read:+(input.read(bs)+ b))
}

我得到了编译错误:

 错误:(55,29)_必须遵循方法;不能跟随字节=> T 
val hh =处理程序(资源)_
^

它是什么是什么意思?

2。

  def readFile [T](f:File)(handler:FileInputStream => Byte => T):T = {
val resource = new java.io.FileInputStream(f)
try {
val hh =处理程序(资源)_
hh(2)
}最后{
resource.close()
}
}

//下半部分是相同的,为了简洁起见删除了...
// ...

结果与否相同。 1,我得到了: _必须遵循方法编译错误。

我读到这是因为下划线用于将方法转换为函数( ETA扩展),但我也看到使用了相同的下划线例如:

  val sum =(x:Int,y)对于部分应用函数 :Int)=> x + y 
val sum2 = sum _

在这种情况下没有错误。



3.

  def readFile [T] (f:File)(handler:FileInputStream => Byte => T):T = {
val resource = new java.io.FileInputStream(f)
try {
val hh = $ {
resource.close()
}
}
$ b // ...

这个工作正常。如果我没有错,这种情况下的下划线称为 ETA扩展,是否正确?但我也从这个Q / A 读取,这种下划线是用于部分应用功能 。在同一个网页中,有人还表示这是占位符语法。那么哪一个是正确的?



4。

 (f:File)(handler:FileInputStream => Byte => T):T = {
val resource = new java.io.FileInputStream(f)
try {
val hh = handler(resource)
hh(2)
} finally {
resource.close()
}
}

// ...

这个不使用下划线,但是它工作得很好,就像没有。 3.我对这个案子的问题,与没有的区别是什么? 3?我应该使用否。 4比没有。 3?是否在下划线中是多余的。 3?



很抱歉,这里有很多问题,但是下划线的东西真的很混乱。



不知何故,我认为Scala中下划线的复杂性与C / C ++中指针和引用(* /& /&&)的复杂性相匹配。



更新: $ b

5。



我再次对下划线发现了一些有趣的内容:

  scala> def sum(x:Int,y:Int)= x + y // sum是一个方法,因为def 
sum:(x:Int,y:Int)Int

scala> val sum2 = sum _ // sum2是来自sum
sum2的显式ETA扩展函数:(Int,Int)=> Int =< function2>

scala> sum2(2,3)//测试
res0:Int = 5

scala> val sum3 =(x:Int,y:Int)=> x + y // sum3是函数对象
sum3:(Int,Int)=> Int =< function2>

scala> val sum4 = sum3 _ //在这里发生了什么?
sum4:()=> (Int,Int)=> Int =< function0>

scala> sum4()(2,3)
res2:Int = 5

你能告诉我发生了什么事 sum4 ?为什么 sum3 _ 的结果具有函数类型:()=> (Int,Int)=> Int



6。

  List(1,2,3)foreach println _ 

根据< a href =https://stackoverflow.com/a/8001065/844005>这个答案,这是部分应用的功能。好的,我可以看到下划线之前的空格有点棘手。它实际上是一样的:

 列表(1,2,3).foreach(println(_))

所以这确实是部分应用的函数。



但是,如果我做到了这一点:

  scala> List(1,2,3).foreach(println _ + 1)//#1 
< console>:8:error:type mismatch;
found:Int(1)
required:String
List(1,2,3).foreach(println _ + 1)
^

阶>列表(1,2,3).foreach(println _ +#)//#2打印出什么都没有(为什么?)

scala> List(1,2,3).foreach(println 1 + _)//#3
< console>:1:error:')'expected,但找到整数字面值。
List(1,2,3).foreach(println 1 + _)
^

scala> List(1,2,3).foreach(println#+ _)//#4
< console>:1:error:')'expected,但找到字符串。
列表(1,2,3).foreach(println#+ _)
^

新手通常会认为下划线在这种情况下是占位符,但我相信它不是,不是吗? 解决方案 / div>

1& 2 - 是相同的,这是 eta-expansion ,这意味着该功能是从函数作为语言的一部分转换为某些 FunctionN 类的实际对象:

 阶> def f(a:Int)= a 
f:(a:Int)Int

scala> f.apply(1)
< console>:9:错误:缺少方法f的参数;
如果您想将其视为部分应用的函数,则使用`_'遵循此方法
f.apply(1)
^
scala> f _
res1:Int => Int =< function1>

scala> (f _)。apply(1)
res2:Int = 1

t在你的例子中作为处理程序(资源)是一个表达式,它返回函数对象 Byte => T (如 handler 是一个函数对象 FileInputStream => Byte => T 并且你部分应用了它),所以scala不能对表达式进行eta-expansion(仅适用于值和方法)。



4 部分应用于scala的curried函数支持的副作用(由 curried 我指的是逐个参数的能力)。

3 仅仅是明确的注意在所有3个例子中 - 你的处理程序:FileInputStream => Byte => T 函数是一个对象(所以它已经被eta扩展了),如果你尝试使用多参数列表方法(它还没有扩展到curried函数)做同样的事情 - 你会收到1& 2& 4的结果相反:

  scala> def f(a:Int)(b:Int)= a //它不是curried函数,因为它只是多参数列表方法
f:(a:Int)(b:Int)Int

scala> f(2)
< console>:9:错误:缺少方法f的参数;
如果您想将其视为部分应用函数,则使用`_'遵循此方法
f(2)
^
scala> f(2)_ //您需要先将f(2)转换为对象
res4:Int => Int =< function1>

scala> f(2)(_)
res5:Int => Int =< function1>

scala> f _ //将方法扩展到函数对象
res6:Int => (Int => Int)=< function1>

因此,如果需要,部分应用程序也会为您执行eta扩展。你也可以考虑eta-expansion作为(不是精确地)用0部分应用的参数的函数,所以它非常接近,因为部分应用的函数总是scala中的对象(在haskell中它是 first-class function ),因为您总是需要部分应用的函数才能头等公民(如object或fc-function)在eta-expansion之后应用它。

5 。 Scala可以为值本身做eta-expansion,因为它们可能被认为是具有0个参数的编译时函数(这就是为什么你看到()=> ... ) 。它可以将任何值扩展到函数对象:

  scala> val k = 5 
k:Int = 5

scala> val kk = k _
kk:()=> Int =< function0>

scala> val kkk = kk _
kkk:()=> ()=> Int =< function0>

scala>

在你的例子中 - value只是另一个函数对象本身。还有(Int,Int)=> Int 不是完全curried函数(它将参数some-count改为some-count),但是scala也可以自动部分应用。为了让它充分受到欢迎:

  scala> def f(a:Int,b:Int)= a 
f:(a:Int,b:Int)Int

scala> (f _)curried
res23:Int => (Int => Int)=< function1>

scala> (a:Int,b:Int)(z:Int)= a
f:(a:Int,b:Int)(z:Int)Int

scala> (f _)。curried
res22:Int => (Int =>(Int => Int))=< function1>

这个过程实际上叫做currying。

另一种使咖啡变得咖啡的方法是使用元组。它不是很纯粹,因为currying实际上是删除元组,但是scala的Tuple只是一个类而不是参数列表中的元组:(Int,Int)=> Int - 输入不是scala术语中的元组,而是在((Int,Int))=> Int ,input是一个元组(不管来自FP-perspecive它是第一种情况下的对象的元组,还是第二种情况下的一个对象的元组)。伪代码示例:

  scala> def f(a:Int,b:Int)= a 
f:(a:Int,b:Int)Int

scala> (f _)。tupled
res24:((Int,Int))=> Int =< function1>

5 vs 1& 2 正如您以前所见, t将eta-expansion应用于表达式,只有方法/值/变量:

  scala> 5 _ 
< console>:8:错误:_必须遵循方法;不能跟随Int(5)
5 _
^

scala> val(a,b)=(5,5)

scala> (a + b)_
< console>:10:错误:_必须遵循方法;不能跟随Int
(a + b)_
^

您会看到方法要求在错误消息中,但scala旨在以相同的方式(至少部分地)支持方法/值/变量(当它们是类/对象的成员时) UAP



6 Function0默认情况下:

  scala> val a = println _ 
a:()=>单位=< function0>

您可能会想到function1,但 println 过载并且eta扩展机制选择最少的签名。当需要其他类型时(例如在 foreach 中的 Function1 ) - 可以选择其他类型:

  scala> val a:String => Unit = println _ 
a:String =>单位=< function1>

正如我所说的,你可能会认为函数对象作为函数部分应用了0个参数(其中包括eta-如果需要扩展),所以这是与另一个混淆的根源答案(我会选择更好的例子)。



正如我在PS2中所说的那样,这个扩展可以自动应用:

  scala> List(1,2)foreach println 
1
2

关于 println _ +# - 它可以工作,因为scala中的任何类(包括 Function1 )都有隐式在Predef中定义的def +(s:String)(这就是为什么 Int 在这里不起作用)(参见 SI-194 ):

 阶> println _ 
res50:()=>单位=< function0>

scala> println _ +#
res51:String =< function0>#

由于 5 vs 1& 2 ,选项不起作用,实际上scala甚至无法在一个参数函数之后解析字符串:

 阶> println#
< console>:1:error:';'expected但找到字符串字面值。
println#
^

您应该指定对象主机来修复它因为scala需要像obj method param这样的东西(但是这是实验性的功能,有时你需要粘贴一些空行或;才能使它工作):

 阶> Predef printlnaaa
aaa

关于C ++参考/指针。函数没有任何价值,因为它是编译时结构,所以编译器只是为它创建一个值,这个过程被称为eta-expansion(或纯粹功能的eta-abstraction)。这个值可能是指针(引用它的对象)的一部分,或者只是引用它自己 - 无关紧要。最重要的是该函数从编译(方法)转移到运行时(f-c函数),因此它变得活跃起来。

P.S.2。有时候,scala会自动执行eta-expansion(如这里)参数列表方法显式作为参数传递。



PSN您可以在@Daniel C. Sobral answer 上找到关于下划线的其他信息。


What is the difference and the term name between these underscore usage from these codes: (see the handler(resource) part)

1.

def readFile[T](f: File)(handler: FileInputStream => Byte => T): T = {
    val resource = new java.io.FileInputStream(f)
    try {
        val hh = handler(resource)_
        hh(2)
    } finally {
        resource.close()
    }
}

val bs = new Array[Byte](4)

readFile(new File("scala.txt")) {
    input => b: Byte => println("Read: " + (input.read(bs) + b))
}

I got compile error:

Error:(55, 29) _ must follow method; cannot follow Byte => T
            val hh = handler(resource)_
                        ^

What does it mean?

2.

def readFile[T](f: File)(handler: FileInputStream => Byte => T): T = {
    val resource = new java.io.FileInputStream(f)
    try {
        val hh = handler(resource) _
        hh(2)
    } finally {
        resource.close()
    }
}

// Lower parts are same, so removed for brevity...
// ...

The result is same as no. 1, I got: _ must follow method compile error.

I read this is because the underscore is used to convert method to function (ETA Expansion), but I also seen the same underscore is used for Partial Applied Function without problem, for example:

val sum = (x: Int, y: Int) => x + y
val sum2 = sum _

No error in this case.

3.

def readFile[T](f: File)(handler: FileInputStream => Byte => T): T = {
    val resource = new java.io.FileInputStream(f)
    try {
        val hh = handler(resource)(_)
        hh(2)
    } finally {
        resource.close()
    }
}

//...

This one works fine. If I'm not wrong, the underscore in this case is called ETA Expansion, is that correct? But I also read from this Q/A, this kind of underscore is for Partial Applied Function. In same page, someone also said this is a Placeholder syntax. So which one is the correct one?

4.

def readFile[T](f: File)(handler: FileInputStream => Byte => T): T = {
    val resource = new java.io.FileInputStream(f)
    try {
        val hh = handler(resource)
        hh(2)
    } finally {
        resource.close()
    }
}

//...

This one doesn't use underscore but it works fine too just like no. 3. My question for this case, what is the difference with no. 3? Should I use no. 4 than no. 3? Is the underscore redundant in no. 3?

Sorry for a lot of questions here, but that underscore thing is really confusing.

Somehow I thought the complexity of underscore in Scala matches the complexity of pointer and reference (*/&/&&) in C/C++.

UPDATE:

5.

I found something interesting again about the underscore:

scala> def sum(x: Int, y: Int) = x + y     // sum is a method because of def
sum: (x: Int, y: Int)Int

scala> val sum2 = sum _    // sum2 is explicit ETA expanded function from sum
sum2: (Int, Int) => Int = <function2>

scala> sum2(2,3)      // testing
res0: Int = 5

scala> val sum3 = (x: Int, y: Int) => x + y      // sum3 is a function object
sum3: (Int, Int) => Int = <function2>

scala> val sum4 = sum3 _           // what happpened here?
sum4: () => (Int, Int) => Int = <function0>

scala> sum4()(2,3)
res2: Int = 5

Could you tell me what happened to sum4? Why the result of sum3 _ has function type: () => (Int, Int) => Int?

6.

List(1, 2, 3) foreach println _

According to this answer, this is Partially applied functions. Ok, I can see that the space before underscore is kinda tricky. It is actually same as:

List(1, 2, 3).foreach(println(_))

So this is indeed Partially applied function.

But if I did this:

scala> List(1, 2, 3).foreach(println _+1)  //#1
<console>:8: error: type mismatch;
 found   : Int(1)
 required: String
              List(1, 2, 3).foreach(println _+1)
                                          ^

scala> List(1, 2, 3).foreach(println _+"#")    //#2 printed out nothing (why?)

scala> List(1, 2, 3).foreach(println 1+_)      //#3
<console>:1: error: ')' expected but integer literal found.
       List(1, 2, 3).foreach(println 1+_)
                                     ^

scala> List(1, 2, 3).foreach(println "#"+_)    //#4
<console>:1: error: ')' expected but string literal found.
       List(1, 2, 3).foreach(println "#"+_)
                                     ^

Newcomer usually will think the underscore in this case as placeholder, but I believe it isn't, isn't it?

解决方案

1 & 2 - are same and this is eta-expansion, which means that function is getting converted from just function as part of language to the real object of some FunctionN class:

scala> def f(a: Int) = a
f: (a: Int)Int

scala> f.apply(1)
<console>:9: error: missing arguments for method f;
follow this method with `_' if you want to treat it as a partially applied function
              f.apply(1)
              ^
scala> f _
res1: Int => Int = <function1>    

scala> (f _).apply(1)
res2: Int = 1

It doesn't work in your example as handler(resource) is an expression which returns function-object Byte => T (as handler is a function-object FileInputStream => Byte => T and you did partial applying on it), so scala can't do eta-expansion for expressions (only for values and methods).

4 is partially applied as side effect of scala's curried functions support (by curried i mean ability to take parameters one-by-one).

3 is just explicitly partially applied.

Note that in all 3 examples - your handler: FileInputStream => Byte => T function is an object (so it's already eta-expanded), if you try to do same things with multi-parameter-list methods (which is not yet expanded to the curried function) - you will receive the opposite results for 1&2&4:

scala> def f(a: Int)(b: Int) = a //it's not a curried function, as it's just multi-parameter-list method
f: (a: Int)(b: Int)Int

scala> f(2) 
<console>:9: error: missing arguments for method f;
follow this method with `_' if you want to treat it as a partially applied function
              f(2)
           ^
scala> f(2) _ //you need to convert f(2) to object first
res4: Int => Int = <function1>

scala> f(2)(_)
res5: Int => Int = <function1>

scala> f _  //expand method to the function object
res6: Int => (Int => Int) = <function1>

So partial application also do an eta-expansion for you if needed. You may also think about eta-expansion as (not precisely) function with 0 partially-applied arguments, so it's pretty much close terms, as partially-applied functions are always objects in scala (in haskell it's first-class function) because you always need partially applied function to be first-class-citizen (like object or f-c-function) to apply it after eta-expansion.

5. Scala can do eta-expansion for values itself, as they may be considered as compile-time functions with 0 parameters (that's why you see () => ...). It can expand any value to the function object:

scala> val k = 5
k: Int = 5

scala> val kk = k _
kk: () => Int = <function0>

scala> val kkk = kk _
kkk: () => () => Int = <function0>

scala> 

In your example - value is just another function-object itself. Also (Int, Int) => Int is not fully curried function (it's taking parameters some-count by some-count), but scala can also do automatical partial applying for such. To make it fully curried:

scala> def f(a: Int, b: Int) = a
f: (a: Int, b: Int)Int

scala> (f _).curried
res23: Int => (Int => Int) = <function1>

scala> def f(a: Int, b: Int)(z: Int) = a
f: (a: Int, b: Int)(z: Int)Int

scala> (f _).curried
res22: Int => (Int => (Int => Int)) = <function1>

This process actually called currying.

Another way to make it curried - is using tuples. It's not so pure as currying is actually removing tuples , but scala's Tuple is just a class and not tuple in parameter list: (Int, Int) => Int - input is not a tuple in scala's terminology, but in ((Int, Int)) => Int, input is a tuple (regardless that from FP-perspecive it's a tuple of to objects in first case and tuple of one object in second). Example of pseudo-tuplying:

 scala> def f(a: Int, b: Int) = a
 f: (a: Int, b: Int)Int

 scala> (f _).tupled
 res24: ((Int, Int)) => Int = <function1>

5 vs 1&2 As you've seen before you can't apply eta-expansion to the expression, only methods/values/vars:

 scala> 5 _
 <console>:8: error: _ must follow method; cannot follow Int(5)
          5 _
          ^

 scala> val (a, b) = (5, 5)

 scala> (a + b) _
 <console>:10: error: _ must follow method; cannot follow Int
              (a + b) _
                 ^

You see the "method" requiring in the error message, but scala aims to treat methods/values/vars (when they are members of class/object) in the same way to (at least partially) support UAP.

6 It's eta expansion which returns Function0 by default:

scala> val a = println _
a: () => Unit = <function0>

You may expect function1 here, but println is overloaded and eta-expansion mechanism choose the fewest signature. When other type is expected (like in Function1 in foreach) - it may choose another:

scala> val a: String => Unit = println _
a: String => Unit = <function1>

As i said you may consider function-object as function partially applied with 0 arguments (which includes eta-expansion if needed), so that's the source of confusion with another answer (i would choose better example there).

As i said in P.S.2 this expansion may be applied automatically:

scala> List(1,2) foreach println
1
2

About println _ +"#" - it works because any class (including Function1) in scala has implicit def + (s: String) (that's why Int doesn't work here) defined in Predef (see SI-194):

scala> println _
res50: () => Unit = <function0>

scala> println _ + "#"
res51: String = <function0>#

Every other options doesn't work because of 5 vs 1&2, actually scala can't even parse string after one-parameter function:

scala> println "#"
<console>:1: error: ';' expected but string literal found.
   println "#"
           ^

You should specify object host to fix it as scala expects something like "obj method param" (but this is experimental feature and sometimes you need to paste some empty lines or ";" to make it work):

scala> Predef println "aaa"
aaa

P.S. About C++ reference/pointer. Function has no value as it's compile-time structure, so compiler is just creating a value for it, that process is called eta-expansion (or eta-abstraction for pure functional). This value may be part of pointer (an object with reference to it) or just reference itself - doesn't matter. What is matter is that function moves from compile (method) to the runtime (f-c-function) here, so it "becomes alive".

P.S.2. Sometimes scala do eta-expansion automatically (like here) when partially applied multi-parameter-list method is explicitly passed as parameter.

P.S.N. You may find some additional info about underscore in @Daniel C. Sobral answer about scala punctuation.

这篇关于这些scala方法中的下划线使用之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆