scala - 函数式编程,Scala 映射和左折叠

标签 scala map functional-programming fold

就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the help center为指导。




8年前关闭。




左折叠有哪些好的教程?

原始问题,从删除中恢复以提供其他答案的上下文:

我正在尝试实现一种方法来查找矩形、圆形、位置和所有扩展形状的组的包围盒。组基本上是一组形状

abstract class Shape  
case class Rectangle(width: Int, height: Int) extends Shape  
case class Location(x: Int, y: Int, shape: Shape) extends Shape  
case class Circle(radius: Int) extends Shape  
case class Group(shape: Shape*) extends Shape  

我得到了除第一组之外的所有三个计算的边界框。所以现在对于边界框方法,我知道我应该为 Group 使用 map 和 fold left,但我无法找到创建它的确切语法。
object BoundingBox {  
  def boundingBox(s: Shape): Location = s match {  
    case Circle(c)=>   
      new Location(-c,-c,s)  
    case Rectangle(_, _) =>  
      new Location(0, 0, s)  
    case Location(x, y, shape) => {  
      val b = boundingBox(shape)  
      Location(x + b.x, y + b.y, b.shape)  
    }  
    case Group(shapes @ _*) =>  ( /: shapes) { } // i dont know how to proceed here.
  }
}

组边界框基本上是包含所有形状的最小边界框。

最佳答案

既然你已经编辑提出一个几乎完全不同的问题,我会给出一个不同的答案。而不是指向有关 map 和折叠的教程,我只会给出一个。

在 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)

所以,这就是一般的 map ,在 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 .也就是说,它需要两件事并返回相同类型的一项。这是一个带有 string-max 函数的示例:
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 创建函数并映射、折叠和缩减它们。因此,如果你知道你的算法应该如何工作,那么实现它应该相当简单。

关于scala - 函数式编程,Scala 映射和左折叠,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2293592/

相关文章:

perl - 为什么我的Perl map 不返回任何内容?

lambda - Java8,将带有条件的for循环转换为lambda

functional-programming - 如何中断、退出撰写或管道?

arrays - Scala 嵌套数组展平

java - 只需指定文件名即可读取不同目录中的文件(通过资源文件夹)

java - 寻找并发 map 支持事件

scala - 如何将构建器模式转换为功能实现?

Scala SetBuffer

scala - 在当前的 Scala 中,是否有更简洁的方法来执行 "lazy constructor pattern"

xml - 文档在运行时在自定义 xslt 映射中没有根元素,但在调试中没有