2012-03-19 48 views
2

第2章新版Manning書籍「Scala in Depth」作者Josh Sueresh發佈了here。在閱讀文章中,我跨越該位的代碼來:被「Scala深度」迷惑選項示例

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = { 

    tmpArg.map(name => new java.io.File(name)). 

      filter(_.isDirectory). 

     getOrElse(new java.io.File(System.getProperty("java.io.tmpdir"))) 

} 

的後續文本說明上述代碼讀取:

的getTemporaryDirectory方法接受命令行參數作爲 含有一個選項字符串並返回一個File對象,該對象引用我們應該使用的臨時目錄 。我們要做的第一件事是在選項上使用 地圖方法創建一個java.io.File,如果有 參數。接下來,我們確保這個新構建的文件對象 是一個目錄。爲此,我們使用過濾器方法。這將檢查 Option中的值是否符合某些謂詞,如果不符合,則將 轉換爲None。最後,我們檢查我們是否有 選項中的值;否則,我們返回默認的臨時目錄。

因此,對於我來自Java和學習Scala的代碼語法而言,我感到困惑。我不明白地圖(...)函數調用後是否有點。看起來有太多的類型推斷髮生,我錯過了某個地方的東西,沒有看到類型。

對於我來說,學習Scala能夠以某種方式看到所有推斷的類型,以便不拖延(或不應用)所有的減少,即看起來像Java之前的冗長版本6類型必須在收集類的平等的兩邊進行解釋。

是否有任何地方需要Scala代碼片段並做出明確不同的事情(可能作爲標誌;一種用於類型,另一種用於隱含,另一種用於大括號,另一種用於分號)。我只需要一些東西將我從完全簡潔的代碼引導到更接近Java的東西,這樣我就可以放心地閱讀(並最終編寫)更多簡潔的Scala。

這裏是什麼樣的,我在尋找:

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = { 

    ContainerType1[Type1] t1 = tmpArg.map(name => new java.io.File(name)); 
    ContainerType2[Type2] t2 = t1.filter(_.isDirectory); 
    return t2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir"))); 

} 

我不是粘在上面具體。我只是無法關注鏈式函數調用如何根據類型推斷實際發生的情況進行工作。任何幫助,將不勝感激。

+0

本文提出了類似於我在上面遇到的挑戰的一點:http://zeroturnaround.com/blog/scala-sink-or-swim-part-1/ – chaotic3quilibrium 2012-03-19 20:40:21

+3

在此級別,您將受益於更多Odersky等人在Scala中編程而不是從Suereth的Scala中深入研究。只是說。 – 2012-03-20 05:29:42

+1

[這篇文章](http://skipoleschris.blogspot.co.uk/2012/03/map-flatten-and-flatmap.html)和漂亮的動畫可能會給你更多的認識 – 2012-03-20 10:02:56

回答

11

好了,你確實有REPL嘗試連鎖命令逐個並檢查他們的結果類型,但我不知道在看到簽名會幫助你這麼多:

scala> Some("c:\\users\\paolo") 
res0: Some[java.lang.String] = Some(c:\users\paolo) 

scala> res0.map(name => new java.io.File(name)) 
res1: Option[java.io.File] = Some(c:\users\paolo) 

scala> res1.filter(_.isDirectory) 
res2: Option[java.io.File] = Some(c:\users\paolo) 

scala> res2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir"))) 
res3: java.io.File = c:\users\paolo 

現在讓我們再試一次,從無。

scala> None:Option[String] 
res6: Option[String] = None 

scala> res6.map(name => new java.io.File(name)) 
res7: Option[java.io.File] = None 

scala> res7.filter(_.isDirectory) 
res8: Option[java.io.File] = None 

scala> res8.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir"))) 
res9: java.io.File = C:\Users\paolo\AppData\Local\Temp 

所以使用Option幫助我們「傳播」的None沒有在每一步檢查空像我們可能會在java中做。

正如你所看到的,這裏並沒有很多類型的推論。 我認爲你的困惑的根源可能在於mapfilter(以及其他)通常與某種類型的集合相關聯,因此可能很難說明他們在Option上做了什麼,這只是與集合類似。 對於我是指你的經典scala.Option cheat sheet

+0

在第三行「scala> res0.map(name => new java.io.File(name))」,變量名稱是從哪裏來的?我猜這應該是res0,對吧? – chaotic3quilibrium 2012-03-19 20:44:24

+0

@ chaotic3quilibrium scala REPL自動創建一個val來分配您輸入的每個表達式的值,並將它們命名爲res0,res1,res2等等。在第一行中,我輸入了'Some(「c:\\ users \\ paolo」)',所以scala自動將它解釋爲'val res0 = Some(「c:\\ users \\ paolo」)'並回答'res0'的類型是'Some [java.lang.String]'並且值'Some(c:\ users \ paolo)'。然後我在'res0'上使用了'map',然後又返回一個'res1'類型爲'Option [java.io.File]'並且值爲'Some(c:\ users \ paolo)'...等等 – 2012-03-19 20:50:42

+0

沒關係,我重新讀它,現在得到res0.map,這個名字是map函數的參數名稱。 – chaotic3quilibrium 2012-03-19 21:18:08

2

拖尾點只是鏈接到下一行上的方法filter。遊戲中沒有推論。

這可以被改寫爲

import java.io._ 

def getTemporaryDirectory(tmpArg : Option[String]) : File = { 
    tmpArg.map(name => new File(name)).filter(_.isDirectory).getOrElse(new File(System.getProperty("java.io.tmpdir"))) 
} 
+0

這不是我的意思。我知道這是一系列的方法調用。我沒有得到的是正在返回哪種類型的下一個方法被調用。類型推斷是隱藏(從我的未經驗的眼睛)每種功能正在返回什麼類型。 – chaotic3quilibrium 2012-03-19 20:22:31

+0

@chaotic - 但是在這個例子中,除了用作方法參數的函數的類型之外,沒有必要進行實際的類型推斷。帶有鏈接調用的Java版本看起來完全像'return tmpArg.map(...)。filter(...)。getOrElse(new File())'只有'...'必須是一些可怕的函數對象實例。 – 2012-03-19 20:39:21

+0

@Michal我知道我可以用Java來做到這一點。但是,一般來說,我知道可以像Java那樣發生的大部分活動。 Scala中有各種各樣的東西,我不明白這會降低我對閱讀和理解序列的信心。我的差距是filter()返回了最後一個點當時使用的Option [File]。 – chaotic3quilibrium 2012-03-19 21:24:46

1

要明確:

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = { 
    val v1: Option[java.io.File] = tmpArg.map(name => new java.io.File(name)) 
    val v2: Option[java.io.File] = v1.filter(_.isDirectory) 
    val v3: File = v2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir"))) 
} 

仍然有很多的類型推斷會在這裏,和丹尼爾Spiewak有an excellent presentation on type inference。它真的讓你瞭解Scala能夠推斷出什麼類型。

更具體地說就是這個例子。

Option[A]map方法有簽名map[B](f: A => B): Option[B]。由於tmpArgOption[String],編譯器知道參數的類型是String => B。現在可以推斷nameString。檢查該功能,可以看到該函數返回File。現在編譯器可以推斷參數的類型是String => File,並且這個調用返回Option[File]

Option[A]filter方法有簽名filter (p: A => Boolean): Option[A]。函數字面值_.isDirectory只是x => x.isDirectory的簡寫。鑑於A現在是File,編譯器可以推斷_也是File。結果是Option[File]

最後,我們有Option[A]getOrElse方法,其簽名爲getOrElse[B >: A](default: => B): B。語法B >: A指定類型參數B約束爲與A相同或超類型。語法=> B將參數指定爲by-name/lazy參數,該參數僅在需要時評估。傳入的參數類型爲File,這意味着BFile,並且此getOrElse返回File

+0

這就是我在尋找的具體問題。所以.map和.filter都返回Option [File]。我可以使用一個工具「取消」這樣的Scala代碼片段的任何想法,所以我可以學習所有的關鍵點來開始推斷自己的類型? – chaotic3quilibrium 2012-03-19 21:05:04

+1

@ chaotic3quilibrium如果您將鼠標懸停在Eclipse中的術語上,它會告訴您它們的類型。 – 2012-03-19 22:17:49

+0

太棒了!我也會盡快嘗試。 Tyvm。 – chaotic3quilibrium 2012-03-20 03:06:41

1

另一個資源和REPL一起使用IDE來分解和/或註釋鏈式表達式。

例如在IntelliJ中,我可以將鼠標懸停在方法調用上並查看它的類型簽名,如果我有可用的源代碼,則可以單擊並查看實現。

但是,重申Daniel的建議,像Scala編程這樣的書會是一個更容易的出發點,涵蓋了類型推斷以及語言的語法規則(這可能會對此示例中的混淆作出更多貢獻)。

+0

我一直在使用Eclipse IDE。但是,我沒有注意到它懸停在代碼片段上時顯示了類型。這非常有幫助。 – chaotic3quilibrium 2012-03-20 13:34:41