既然您已经编辑了一个几乎完全不同的问题,我将给出一个不同的答案。而不是指向一个关于地图和折叠的教程,我只会给出一个。
在 Scala 中,您首先需要知道如何创建匿名函数。它是这样的,从最一般到更具体:
(var1: Type1, var2: Type2, ..., varN: TypeN) => /* output */
(var1, var2, ..., varN) => /* output, if types can be inferred */
var1 => /* output, if type can be inferred and N=1 */
这里有些例子:
(x: Double, y: Double, z: Double) => Math.sqrt(x*x + y*y + z*z)
val f:(Double,Double)=>Double = (x,y) => x*y + Math.exp(-x*y)
val neg:Double=>Double = x => -x
现在,map
列表等方法将对地图的每个元素应用一个函数(匿名或其他)。也就是说,如果你有
List(a1,a2,...,aN)
f:A => B
然后
List(a1,a2,...,aN) map (f)
生产
List( f(a1) , f(a2) , ..., f(aN) )
这可能有用的原因有很多。也许你有一堆字符串,你想知道每个字符串有多长,或者你想让它们全部大写,或者你想让它们倒过来。如果您有一个函数可以对一个元素执行您想要的操作,则 map 将对所有元素执行此操作:
scala> List("How","long","are","we?") map (s => s.length)
res0: List[Int] = List(3, 4, 3, 3)
scala> List("How","capitalized","are","we?") map (s => s.toUpperCase)
res1: List[java.lang.String] = List(HOW, CAPITALIZED, ARE, WE?)
scala> List("How","backwards","are","we?") map (s => s.reverse)
res2: List[scala.runtime.RichString] = List(woH, sdrawkcab, era, ?ew)
所以,这就是一般的地图,在 Scala 中也是如此。
但是如果我们想收集我们的结果呢?这就是 fold 出现的地方(foldLeft
作为从左侧开始并向右工作的版本)。
假设我们有一个函数f:(B,A) => B
,也就是说,它需要一个 B 和一个 A,并将它们组合起来产生一个 B。好吧,我们可以从一个 B 开始,然后一次将一个 A 的列表输入它,然后在最后,我们会得到一些 B。这正是 fold 所做的。 foldLeft
是否从列表的左端开始;foldRight
从右边开始。那是,
List(a1,a2,...,aN) foldLeft(b0)(f)
生产
f( f( ... f( f(b0,a1) , a2 ) ... ), aN )
b0
当然,你的初始值在哪里。
所以,也许我们有一个函数,它接受一个 int 和一个字符串,并返回 int 或字符串的长度,以较大者为准——如果我们使用它折叠我们的列表,它会告诉我们最长的字符串(假设我们从 0 开始)。或者我们可以将长度添加到 int 中,同时累积值。
试一试吧。
scala> List("How","long","is","longest?").foldLeft(0)((i,s) => i max s.length)
res3: Int = 8
scala> List("How","long","is","everyone?").foldLeft(0)((i,s) => i + s.length)
res4: Int = 18
好吧,好吧,但是如果我们想知道谁是最长的呢?一种方法(也许不是最好的,但它很好地说明了一个有用的模式)是同时携带长度(整数)和领先的竞争者(字符串)。让我们试一试:
scala> List("Who","is","longest?").foldLeft((0,""))((i,s) =>
| if (i._1 < s.length) (s.length,s)
| else i
| )
res5: (Int, java.lang.String) = (8,longest?)
在这里,i
现在是一个类型为 的元组(Int,String)
,并且i._1
是该元组的第一部分(一个 Int)。
但在某些情况下,使用折叠并不是我们真正想要的。如果我们想要两个字符串中较长的一个,最自然的函数就是max:(String,String)=>String
. 我们如何应用那个?
好吧,在这种情况下,有一个默认的“最短”情况,所以我们可以折叠以“”开头的 string-max 函数。但更好的方法是使用reduce。与折叠一样,有两个版本,一个从左侧工作,另一个从右侧工作。它没有初始值,并且需要一个函数f:(A,A)=>A
。也就是说,它需要两个东西并返回一个相同类型的东西。这是一个带有字符串最大值函数的示例:
scala> List("Who","is","longest?").reduceLeft((s1,s2) =>
| if (s2.length > s1.length) s2
| else s1
| )
res6: java.lang.String = longest?
现在,还有两个技巧。首先,以下两个意思相同:
list.foldLeft(b0)(f)
(b0 /: list)(f)
注意第二个是如何变短的,它给你的印象是你正在b0
用它(你就是)对列表做一些事情。(:\
与 相同foldRight
,但您可以像这样使用它:(list :\ b0) (f)
其次,如果你只引用一个变量一次,你可以使用_
代替变量名并省略x =>
匿名函数声明的部分。这里有两个例子:
scala> List("How","long","are","we?") map (_.length)
res7: List[Int] = List(3, 4, 3, 3)
scala> (0 /: List("How","long","are","we","all?"))(_ + _.length)
res8: Int = 16
此时,您应该能够使用 Scala 创建函数并映射、折叠和归约它们。因此,如果您知道您的算法应该如何工作,那么实现它应该相当简单。