亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Scala foreach奇怪的行為

Scala foreach奇怪的行為

LEATH 2019-11-30 13:47:33
我想在Scala中使用漂亮的單行代碼遍歷值列表。例如,這很好用:scala> val x = List(1,2,3,4)x: List[Int] = List(1, 2, 3, 4)scala> x foreach println1234但是,如果我使用占位符_,則會給我一個錯誤:scala> x foreach println(_ + 1)<console>:6: error: missing parameter type for expanded function ((x$1) =>x$1.$plus(1))       x foreach println(_ + 1)                         ^
查看完整描述

3 回答

?
慕雪6442864

TA貢獻1812條經驗 獲得超5個贊

這個:


x foreach println(_ + 1)

等效于此:


x.foreach(println(x$1 => x$1 + 1))

沒有跡象表明的類型可能是什么x$1,并且說實話,打印函數沒有任何意義。


您(對我而言)顯然打算打印x$0 + 1,而x$0參數傳遞到的位置foreach。但是,讓我們考慮一下...... foreach以a作為參數Function1[T, Unit],其中T是列表的類型參數。foreach相反println(_ + 1),您傳遞給的是返回的表達式Unit。


如果您寫了,相反x foreach println,您將傳遞完全不同的東西。您將傳遞function(*)println,該函數接受Any并返回Unit,因此符合的要求foreach。


由于的擴展規則,這有點令人困惑_。它擴展到最里面的表達式定界符(括號或花括號),除非它們代替了參數,在這種情況下,它意味著另一件事:部分函數應用程序。


為了更好地解釋這一點,請看以下示例:


def f(a: Int, b: Int, c: Int) = a + b + c

val g: Int => Int = f(_, 2, 3) // Partial function application

g(1)

在這里,我們將第二個和第三個參數應用于f,并返回一個僅需要剩余參數的函數。請注意,它只能按原樣工作,因為我指出了的類型g,否則我必須指出未應用的參數的類型。讓我們繼續:


val h: Int => Int = _ + 1 // Anonymous function, expands to (x$1: Int => x$1 + 1)

val i: Int => Int = (_ + 1) // Same thing, because the parenthesis are dropped here

val j: Int => Int = 1 + (_ + 1) // doesn't work, because it expands to 1 + (x$1 => x$1 + 1), so it misses the type of `x$1`

val k: Int => Int = 1 + ((_: Int) + 1) // doesn't work, because it expands to 1 + (x$1: Int => x$1 + 1), so you are adding a function to an `Int`, but this operation doesn't exist

讓我們k更詳細地討論,因為這是非常重要的一點?;叵胍幌逻@g是一個函數Int => Int,對嗎?所以,如果我要輸入1 + g,這有意義嗎?這就是在中所做的k。


使人們感到困惑的是他們真正想要的是:


val j: Int => Int = x$1 => 1 + (x$1 + 1)

換句話說,他們希望x$1替換對象_跳到括號之外,并跳到正確的位置。這里的問題是,盡管對他們來說合適的地方似乎很明顯,但對于編譯器來說卻并不明顯。考慮以下示例,例如:


def findKeywords(keywords: List[String], sentence: List[String]) = sentence.filter(keywords contains _.map(_.toLowerCase))

現在,如果將其擴展到括號之外,我們將得到:


def findKeywords(keywords: List[String], sentence: List[String]) = (x$1, x$2) => sentence.filter(keywords contains x$1.map(x$2.toLowerCase))

這絕對不是我們想要的。事實上,如果_沒有得到由最里面的表達式分隔符為界,一個永遠無法使用_嵌套map,flatMap,filter和foreach。


現在,回到匿名函數和部分應用程序之間的混淆,請看這里:


List(1,2,3,4) foreach println(_) // doesn't work

List(1,2,3,4) foreach (println(_)) // works

List(1,2,3,4) foreach (println(_ + 1)) // doesn't work

由于操作符號的工作方式,第一行不起作用。Scala只是看到printlnreturn Unit,這不是foreach期望的。


第二行之所以起作用,是因為括號使Scala println(_)可以整體評估。這是一個部分函數應用程序,因此它返回Any => Unit,這是可以接受的。


第三行不起作用,因為它_ + 1是匿名函數,您將其作為參數傳遞給println。您并沒有成為println匿名函數的一部分,而這正是您想要的。


最后,很少有人期望:


List(1,2,3,4) foreach (Console println _ + 1)

這可行。為什么這樣做留給讀者練習。:-)


(*)實際上println是一種方法。在編寫時x foreach println,您沒有在傳遞方法,因為無法傳遞方法。相反,Scala創建一個閉包并將其傳遞。它像這樣擴展:


x.foreach(new Function1[Any,Unit] { def apply(x$1: Any): Unit = Console.println(x$1) })


查看完整回答
反對 回復 2019-11-30
?
拉風的咖菲貓

TA貢獻1995條經驗 獲得超2個贊

下劃線有點棘手。根據規范,該短語:


_ + 1

相當于


x => x + 1


x foreach println (y => y + 1)

產量:


<console>:6: error: missing parameter type

           x foreach println (y => y + 1)

如果您在其中添加一些類型:


x foreach( println((y:Int) => y + 1))

<console>:6: error: type mismatch;

 found   : Unit

 required: (Int) => Unit

           x foreach( println((y:Int) => y + 1))

問題是您要傳遞一個匿名函數給println它,而它不能處理它。您真正想要做的是(如果您嘗試將繼承者打印到列表中的每個項目上):


x map (_+1) foreach println


查看完整回答
反對 回復 2019-11-30
?
喵喵時光機

TA貢獻1846條經驗 獲得超7個贊

Welcome to Scala version 2.8.0.Beta1-prerelease (Java HotSpot(TM) Client VM, Java 1.6.0_17).

Type in expressions to have them evaluated.

Type :help for more information.


scala> val l1 = List(1, 2, 3)

l1: List[Int] = List(1, 2, 3)


scala>


scala> l1.foreach(println(_))

1

2

3


查看完整回答
反對 回復 2019-11-30
  • 3 回答
  • 0 關注
  • 1131 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號