既然您已经编辑了问题以提出完全不同的问题,我将给出另一个答案。与其指向一个关于Maps和Folds的教程,不如直接给出一个。
在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
方法将会对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中使用的map。
但是如果我们想要收集我们的结果怎么办?这就是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
是您的初始值。
所以,我们可能有一个函数,它接受一个整数和一个字符串,并返回整数或字符串的长度,以较大者为准--如果我们使用这个函数对列表进行折叠,它将告诉我们最长的字符串(假设我们从0开始)。 或者我们可以将长度加到整数中,随着我们前进而累积值。
让我们试一下。
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)。
但在某些情况下,像这样使用 fold 并不是我们想要的。如果我们想要两个字符串中较长的那个,最自然的函数就是类似于max:(String,String)=>String
的函数。我们如何应用它呢?
好吧,在这种情况下,有一个默认的“最短”情况,所以我们可以从 "" 开始折叠字符串-max函数。但更好的方法是使用reduce。与fold一样,有两个版本,一个从左边开始工作,另一个从右边开始工作。它不需要初始值,并且需要一个函数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创建函数并进行映射、折叠和缩减操作。因此,如果你知道你的算法应该如何工作,那么实现它应该是相当简单的。
foldLeft
的问题,而是关于算法的问题。你最好问一下:“如何使用不可变数据结构从形状列表计算出最小边界框?”将问题标记为与语言无关和算法相关。也许还可以加上函数式编程。如果你在实现建议的算法时遇到问题,那么就打开一个Scala问题。当前的问题针对了错误的组。 - Daniel C. Sobral