除了丹尼爾的回答之外,請記住Stream
對於短路評估很有用。例如,假設我有一個巨大的一套採取String
並返回Option[String]
功能,我想繼續執行,直到其中的一個作品:
val stringOps = List(
(s:String) => if (s.length>10) Some(s.length.toString) else None ,
(s:String) => if (s.length==0) Some("empty") else None ,
(s:String) => if (s.indexOf(" ")>=0) Some(s.trim) else None
);
嘛,我當然不希望執行整個列表,並且在List
上沒有任何方便的方法,它說:「將它們作爲函數處理並執行它們,直到它們中的一個返回除None
以外的其他值」。該怎麼辦?也許這:
def transform(input: String, ops: List[String=>Option[String]]) = {
ops.toStream.map(_(input)).find(_ isDefined).getOrElse(None)
}
這需要一個列表,並把它當作一個Stream
(實際上不評價任何東西),然後定義一個新的Stream
作爲應用功能的結果(但不評估任何東西),然後搜索第一個被定義的 - 在這裏,神奇地,它回顧並且意識到它必須應用該地圖,並從原始列表中獲得正確的數據 - 然後從Option[Option[String]]
中解開它到Option[String]
使用getOrElse
。
下面是一個例子:
scala> transform("This is a really long string",stringOps)
res0: Option[String] = Some(28)
scala> transform("",stringOps)
res1: Option[String] = Some(empty)
scala> transform(" hi ",stringOps)
res2: Option[String] = Some(hi)
scala> transform("no-match",stringOps)
res3: Option[String] = None
但它的工作原理?如果我們把一個println
到我們的功能,所以我們可以告訴我們,如果他們是所謂的,我們得到
val stringOps = List(
(s:String) => {println("1"); if (s.length>10) Some(s.length.toString) else None },
(s:String) => {println("2"); if (s.length==0) Some("empty") else None },
(s:String) => {println("3"); if (s.indexOf(" ")>=0) Some(s.trim) else None }
);
// (transform is the same)
scala> transform("This is a really long string",stringOps)
1
res0: Option[String] = Some(28)
scala> transform("no-match",stringOps)
1
2
3
res1: Option[String] = None
(這是斯卡拉2.8; 2.7的實現有時會通過一個超調,遺憾的是並注意您。 做積累的None
很長的列表,你的失敗累積,但據推測相比,這裏你真正的計算,這是廉價的。)
我想補充這另一個區別是,'Stream'從來沒有在它的頭部元素懶惰。 「流」的頭部以評估形式存儲。如果需要一個沒有元素(包括頭部)直到被請求的序列,那麼'Iterator'是唯一的選擇。 – Lii 2016-08-07 08:37:55
除了頭元素不懶惰之外,它還評估你想要放下的每個元素。例如:' 「一個」 #:: 「B」 #:: 「C」 #:: 「d」 #:: Stream.empy [字符串] .drop(3)'將評價 「一」, 「B」,「 c「和」d「。 「d」因爲它變成了頭。 – r90t 2016-08-13 13:21:23
素數生成器的有趣簡潔示例。有趣的是,如果我在一個簡單的Scala控制檯中創建它,然後請求4000個素數(在實踐中沒有那麼多,我有一個可以在不到2秒內創建100K的替代定義),它會導致Scala出現「GC overhead overhead exceeded」錯誤。 – 2018-02-06 20:24:53