使用 Map 而不是 List 理解 foldLeft
Understanding foldLeft with Map instead of List
我很想了解 foldLeft
地图的工作原理。如果我有一个 List 并使用零元素和函数对其调用 foldLeft,我确实理解它是如何工作的:
val list1 = List(1,2,3)
list1.foldLeft(0)((a,b) => a + b)
我将零元素 0
添加到 list1
的第一个元素,然后添加 list1
的第二个元素,依此类推。所以输出成为新的输入,第一个输入是零元素。
现在我得到了代码
val map1 = Map(1 -> 2.0, 3 -> 4.0, 5 -> 6.2) withDefaultValue 0.0
val map2 = Map(0 -> 3.0, 3 -> 7.0) withDefaultValue 0.0
def myfct(terms: Map[Int, Double], term: (Int, Double)): Map[Int, Double] = ???
map1.foldLeft(map2)(myfct)
- 所以我这里的第一个元素是
Tuple2
,但是由于 map2
是 Map
而不是 Tuple2
,零元素是什么?
- 当我们有一个
List
,即list1
,我们总是"took the next element in list1
"。 map1
中的“下一个元素是什么?是另一对map1
吗?
在这种情况下,您可以将 Map
视为元组列表。您可以像这样创建一个列表:List(1 -> 2.0, 3 -> 4.0, 5 -> 6.2)
,然后在其上调用 foldLeft
(这或多或少正是 Map.foldLeft
所做的)。如果您了解 foldLeft
如何处理列表,那么现在您也知道它如何处理地图 :)
回答您的具体问题:
foldLeft
的第一个参数可以是任意类型。您也可以在第一个示例中传入 Map
而不是 Int
。它不必与您正在处理的集合的元素具有相同的类型(尽管可能是),就像您在第一个示例中那样,也不需要与集合本身具有相同的类型,就像您在最后一个例子中有它。
举个例子:
List(1,2,3,4,5,6).foldLeft(Map.empty[String,Int]) { case(map,elem) =>
map + (elem.toString -> elem)
}
这会产生与 list.map { x => x.toString -> x }.toMap
相同的结果。如您所见,这里的第一个参数是 Map
,既不是 List
也不是 Int
.
你传给foldLeft
的类型也是它returns的类型,你传入的函数也是returns的类型。它不是“元素零”。
foldLeft
将该参数连同列表的第一个元素传递给您的 reducer 函数。您的函数将组合这两个元素,并生成与第一个参数类型相同的新值。该值再次传入,再次使用第二个元素......等等。
也许,检查 foldLeft
的签名会有所帮助:
foldLeft[B](z: B)(op: (B, A) ⇒ B): B
这里A
是你的集合元素的类型,B
可以是任何东西,唯一的要求是它出现的四个地方是相同的类型。
这是另一个例子,(几乎)等同于 list.mkString(",")
:
List(1,2,3,4,5,6).foldLeft("") {
case("", i) => i.toString
case(s,i) => s + "," + i
}
正如我在开头所解释的那样,此上下文中的映射是一种列表(而不是序列)。就像处理列表时“我们总是取列表的下一个元素”一样,在这种情况下我们将取“地图的下一个元素”。你自己说了,map的元素是元组,所以下一个元素的类型就是:
Map("one" -> 1, "two" -> 2, "three" -> 3)
.foldLeft("") {
case("", (key,value)) => key + "->" + value
case(s, (key,value)) => s + ", " + key + "->" + value
}
如上所述,foldLeft
操作的签名如图:
foldLeft[B](z: B)(op: (B, A) ⇒ B): B
操作的结果类型是B
。所以,这回答了你的第一个问题:
So my first element here is a Tuple2, but since map2 is a Map and not
a Tuple2, what is the zero-element?
零元素是你想从foldLeft
输出的任何东西。它可以是 Int
或 Map
或任何其他与此相关的内容。
函数签名(即第二个参数)中的 op
是您希望对 map1 的每个元素进行操作的方式,它是一个 (key,value)
对,或者另一种方式写成 key -> value
.
让我们尝试通过更简单的操作构建它来理解它。
val map1 = Map(1 -> 1.0, 4 -> 4.0, 5 -> 5.0) withDefaultValue 0.0
val map2 = Map(0 -> 0.0, 3 -> 3.0) withDefaultValue 0.0
// Here we're just making the output an Int
// So we just add the first element of the key-value pair.
def myOpInt(z: Int, term: (Int, Double)): Int = {
z + term._1
}
// adds all the first elements of the key-value pairs of map1 together
map1.foldLeft(0)(myOpInt) // ... output is 10
// same as above, but for map2 ...
map2.foldLeft(0)(myOpInt) // ... output is 3
现在,通过使用零元素 (z
) 作为现有映射将其带入下一步...我们基本上将元素添加到我们用作 z
的映射中。
val map1 = Map(1 -> 1.0, 4 -> 4.0, 5 -> 5.0) withDefaultValue 0.0
val map2 = Map(0 -> 0.0, 3 -> 3.0) withDefaultValue 0.0
// Here z is a Map of Int -> Double
// We expect a pair of (Int, Double) as the terms to fold left with z
def myfct(z: Map[Int, Double], term: (Int, Double)): Map[Int, Double] = {
z + term
}
map1.foldLeft(map2)(myfct)
// output is Map(0 -> 0.0, 5 -> 5.0, 1 -> 1.0, 3 -> 3.0, 4 -> 4.0)
// i.e. same elements of both maps concatenated together, albeit out of order, since Maps don't guarantee order ...
When we had a List, namely list1, we always "took the next element in
list1". What is the "next element in map1? Is it another pair of map1?
是的,这是 map1
中的另一个键值对 (k,v)
。
我很想了解 foldLeft
地图的工作原理。如果我有一个 List 并使用零元素和函数对其调用 foldLeft,我确实理解它是如何工作的:
val list1 = List(1,2,3)
list1.foldLeft(0)((a,b) => a + b)
我将零元素 0
添加到 list1
的第一个元素,然后添加 list1
的第二个元素,依此类推。所以输出成为新的输入,第一个输入是零元素。
现在我得到了代码
val map1 = Map(1 -> 2.0, 3 -> 4.0, 5 -> 6.2) withDefaultValue 0.0
val map2 = Map(0 -> 3.0, 3 -> 7.0) withDefaultValue 0.0
def myfct(terms: Map[Int, Double], term: (Int, Double)): Map[Int, Double] = ???
map1.foldLeft(map2)(myfct)
- 所以我这里的第一个元素是
Tuple2
,但是由于map2
是Map
而不是Tuple2
,零元素是什么? - 当我们有一个
List
,即list1
,我们总是"took the next element inlist1
"。map1
中的“下一个元素是什么?是另一对map1
吗?
在这种情况下,您可以将 Map
视为元组列表。您可以像这样创建一个列表:List(1 -> 2.0, 3 -> 4.0, 5 -> 6.2)
,然后在其上调用 foldLeft
(这或多或少正是 Map.foldLeft
所做的)。如果您了解 foldLeft
如何处理列表,那么现在您也知道它如何处理地图 :)
回答您的具体问题:
foldLeft
的第一个参数可以是任意类型。您也可以在第一个示例中传入Map
而不是Int
。它不必与您正在处理的集合的元素具有相同的类型(尽管可能是),就像您在第一个示例中那样,也不需要与集合本身具有相同的类型,就像您在最后一个例子中有它。 举个例子:List(1,2,3,4,5,6).foldLeft(Map.empty[String,Int]) { case(map,elem) => map + (elem.toString -> elem) }
这会产生与 list.map { x => x.toString -> x }.toMap
相同的结果。如您所见,这里的第一个参数是 Map
,既不是 List
也不是 Int
.
你传给foldLeft
的类型也是它returns的类型,你传入的函数也是returns的类型。它不是“元素零”。
foldLeft
将该参数连同列表的第一个元素传递给您的 reducer 函数。您的函数将组合这两个元素,并生成与第一个参数类型相同的新值。该值再次传入,再次使用第二个元素......等等。
也许,检查 foldLeft
的签名会有所帮助:
foldLeft[B](z: B)(op: (B, A) ⇒ B): B
这里A
是你的集合元素的类型,B
可以是任何东西,唯一的要求是它出现的四个地方是相同的类型。
这是另一个例子,(几乎)等同于 list.mkString(",")
:
List(1,2,3,4,5,6).foldLeft("") {
case("", i) => i.toString
case(s,i) => s + "," + i
}
正如我在开头所解释的那样,此上下文中的映射是一种列表(而不是序列)。就像处理列表时“我们总是取列表的下一个元素”一样,在这种情况下我们将取“地图的下一个元素”。你自己说了,map的元素是元组,所以下一个元素的类型就是:
Map("one" -> 1, "two" -> 2, "three" -> 3) .foldLeft("") { case("", (key,value)) => key + "->" + value case(s, (key,value)) => s + ", " + key + "->" + value }
如上所述,foldLeft
操作的签名如图:
foldLeft[B](z: B)(op: (B, A) ⇒ B): B
操作的结果类型是B
。所以,这回答了你的第一个问题:
So my first element here is a Tuple2, but since map2 is a Map and not a Tuple2, what is the zero-element?
零元素是你想从foldLeft
输出的任何东西。它可以是 Int
或 Map
或任何其他与此相关的内容。
op
是您希望对 map1 的每个元素进行操作的方式,它是一个 (key,value)
对,或者另一种方式写成 key -> value
.
让我们尝试通过更简单的操作构建它来理解它。
val map1 = Map(1 -> 1.0, 4 -> 4.0, 5 -> 5.0) withDefaultValue 0.0
val map2 = Map(0 -> 0.0, 3 -> 3.0) withDefaultValue 0.0
// Here we're just making the output an Int
// So we just add the first element of the key-value pair.
def myOpInt(z: Int, term: (Int, Double)): Int = {
z + term._1
}
// adds all the first elements of the key-value pairs of map1 together
map1.foldLeft(0)(myOpInt) // ... output is 10
// same as above, but for map2 ...
map2.foldLeft(0)(myOpInt) // ... output is 3
现在,通过使用零元素 (z
) 作为现有映射将其带入下一步...我们基本上将元素添加到我们用作 z
的映射中。
val map1 = Map(1 -> 1.0, 4 -> 4.0, 5 -> 5.0) withDefaultValue 0.0
val map2 = Map(0 -> 0.0, 3 -> 3.0) withDefaultValue 0.0
// Here z is a Map of Int -> Double
// We expect a pair of (Int, Double) as the terms to fold left with z
def myfct(z: Map[Int, Double], term: (Int, Double)): Map[Int, Double] = {
z + term
}
map1.foldLeft(map2)(myfct)
// output is Map(0 -> 0.0, 5 -> 5.0, 1 -> 1.0, 3 -> 3.0, 4 -> 4.0)
// i.e. same elements of both maps concatenated together, albeit out of order, since Maps don't guarantee order ...
When we had a List, namely list1, we always "took the next element in list1". What is the "next element in map1? Is it another pair of map1?
是的,这是 map1
中的另一个键值对 (k,v)
。