Scala:比较两个数组中相同位置的元素
Scala: Compare elements at same position in two arrays
我正在学习 Scala,正在尝试编写某种函数,将列表中的一个元素与另一个列表中的元素进行比较在同一索引.我知道必须有比两个写两个 for
循环并手动跟踪每个循环的当前 index
更 Scalic 的方法来做到这一点。
例如,假设我们正在比较 URL。假设我们有以下两个 List
s,它们是由 /
字符分割的 URL:
val incomingUrl = List("users", "profile", "12345")
和
val urlToCompare = List("users", "profile", ":id")
假设我想将任何以 :
字符开头的元素视为匹配项,但任何不以 :
字符开头的元素都不是匹配项。
进行此比较的最佳 和最 Scalatic 方法是什么?
来自 OOP 背景,我会立即跳到 for
循环,但我知道必须有一个好的 FP 方法来解决它,这将教会我一两件关于 Scala 的事情.
编辑
为了完成,我发现 在发布与该问题相关的我的帖子后不久。
编辑 2
我为这个特定用例选择的实现:
def doRoutesMatch(incomingURL: List[String], urlToCompare: List[String]): Boolean = {
// if the lengths don't match, return immediately
if (incomingURL.length != urlToCompare.length) return false
// merge the lists into a tuple
urlToCompare.zip(incomingURL)
// iterate over it
.foreach {
// get each path
case (existingPath, pathToCompare) =>
if (
// check if this is some value supplied to the url, such as `:id`
existingPath(0) != ':' &&
// if this isn't a placeholder for a value that the route needs, then check if the strings are equal
p2 != p1
)
// if neither matches, it doesn't match the existing route
return false
}
// return true if a `false` didn't get returned in the above foreach loop
true
}
您可以使用 zip
,在 Seq[A]
上调用 Seq[B]
结果是 Seq[(A, B)]
。换句话说,它创建了一个包含两个序列元素的元组的序列:
incomingUrl.zip(urlToCompare).map { case(incoming, pattern) => f(incoming, pattern) }
这个问题已经有另一个答案,但我要添加另一个答案,因为有一个极端情况需要注意。如果你不知道两个List的长度,你需要zipAll。如果 List 中不存在相应的元素,zipAll 需要插入一个默认值,因此我首先将每个元素包装在一个 Some 中,然后执行 zipAll。
object ZipAllTest extends App {
val incomingUrl = List("users", "profile", "12345", "extra")
val urlToCompare = List("users", "profile", ":id")
val list1 = incomingUrl.map(Some(_))
val list2 = urlToCompare.map(Some(_))
val zipped = list1.zipAll(list2, None, None)
println(zipped)
}
可能困扰您的一件事是我们要对数据进行多次传递。如果这是您担心的事情,您可以使用惰性集合或编写一个只传递一次数据的自定义折叠。不过,这可能有点矫枉过正。如果有人愿意,他们可以在另一个答案中添加这些替代实现。
由于 OP 很想知道我们如何使用惰性集合或自定义折叠来做同样的事情,我在这些实现中包含了一个单独的答案。
第一个实现使用惰性集合。请注意,惰性集合的缓存属性很差,因此在实践中,将惰性集合用作微优化通常没有意义。尽管惰性集合会最大限度地减少遍历数据的次数,但正如已经提到的那样,底层数据结构没有良好的缓存局部性。要了解为什么惰性集合会最大限度地减少您对数据进行的传递次数,请阅读 Scala 中的函数式编程.
的第 5 章
object LazyZipTest extends App{
val incomingUrl = List("users", "profile", "12345", "extra").view
val urlToCompare = List("users", "profile", ":id").view
val list1 = incomingUrl.map(Some(_))
val list2 = urlToCompare.map(Some(_))
val zipped = list1.zipAll(list2, None, None)
println(zipped)
}
第二种实现使用自定义折叠仅遍历列表一次。由于我们要追加到数据结构的尾部,所以我们要使用 IndexedSeq,而不是 List。无论如何,您应该很少使用 List。否则,如果您要从 List 转换为 IndexedSeq,您实际上是在对数据进行一次额外的传递,在这种情况下,您最好不要打扰,只需使用我已经在另一个答案中写过的简单实现。
这是自定义折叠。
object FoldTest extends App{
val incomingUrl = List("users", "profile", "12345", "extra").toIndexedSeq
val urlToCompare = List("users", "profile", ":id").toIndexedSeq
def onePassZip[T, U](l1: IndexedSeq[T], l2: IndexedSeq[U]): IndexedSeq[(Option[T], Option[U])] = {
val folded = l1.foldLeft((l2, IndexedSeq[(Option[T], Option[U])]())) { (acc, e) =>
acc._1 match {
case x +: xs => (xs, acc._2 :+ (Some(e), Some(x)))
case IndexedSeq() => (IndexedSeq(), acc._2 :+ (Some(e), None))
}
}
folded._2 ++ folded._1.map(x => (None, Some(x)))
}
println(onePassZip(incomingUrl, urlToCompare))
println(onePassZip(urlToCompare, incomingUrl))
}
大家有什么问题,我可以在评论区一一解答。
我正在学习 Scala,正在尝试编写某种函数,将列表中的一个元素与另一个列表中的元素进行比较在同一索引.我知道必须有比两个写两个 for
循环并手动跟踪每个循环的当前 index
更 Scalic 的方法来做到这一点。
例如,假设我们正在比较 URL。假设我们有以下两个 List
s,它们是由 /
字符分割的 URL:
val incomingUrl = List("users", "profile", "12345")
和
val urlToCompare = List("users", "profile", ":id")
假设我想将任何以 :
字符开头的元素视为匹配项,但任何不以 :
字符开头的元素都不是匹配项。
进行此比较的最佳 和最 Scalatic 方法是什么?
来自 OOP 背景,我会立即跳到 for
循环,但我知道必须有一个好的 FP 方法来解决它,这将教会我一两件关于 Scala 的事情.
编辑
为了完成,我发现
编辑 2
我为这个特定用例选择的实现:
def doRoutesMatch(incomingURL: List[String], urlToCompare: List[String]): Boolean = {
// if the lengths don't match, return immediately
if (incomingURL.length != urlToCompare.length) return false
// merge the lists into a tuple
urlToCompare.zip(incomingURL)
// iterate over it
.foreach {
// get each path
case (existingPath, pathToCompare) =>
if (
// check if this is some value supplied to the url, such as `:id`
existingPath(0) != ':' &&
// if this isn't a placeholder for a value that the route needs, then check if the strings are equal
p2 != p1
)
// if neither matches, it doesn't match the existing route
return false
}
// return true if a `false` didn't get returned in the above foreach loop
true
}
您可以使用 zip
,在 Seq[A]
上调用 Seq[B]
结果是 Seq[(A, B)]
。换句话说,它创建了一个包含两个序列元素的元组的序列:
incomingUrl.zip(urlToCompare).map { case(incoming, pattern) => f(incoming, pattern) }
这个问题已经有另一个答案,但我要添加另一个答案,因为有一个极端情况需要注意。如果你不知道两个List的长度,你需要zipAll。如果 List 中不存在相应的元素,zipAll 需要插入一个默认值,因此我首先将每个元素包装在一个 Some 中,然后执行 zipAll。
object ZipAllTest extends App {
val incomingUrl = List("users", "profile", "12345", "extra")
val urlToCompare = List("users", "profile", ":id")
val list1 = incomingUrl.map(Some(_))
val list2 = urlToCompare.map(Some(_))
val zipped = list1.zipAll(list2, None, None)
println(zipped)
}
可能困扰您的一件事是我们要对数据进行多次传递。如果这是您担心的事情,您可以使用惰性集合或编写一个只传递一次数据的自定义折叠。不过,这可能有点矫枉过正。如果有人愿意,他们可以在另一个答案中添加这些替代实现。
由于 OP 很想知道我们如何使用惰性集合或自定义折叠来做同样的事情,我在这些实现中包含了一个单独的答案。
第一个实现使用惰性集合。请注意,惰性集合的缓存属性很差,因此在实践中,将惰性集合用作微优化通常没有意义。尽管惰性集合会最大限度地减少遍历数据的次数,但正如已经提到的那样,底层数据结构没有良好的缓存局部性。要了解为什么惰性集合会最大限度地减少您对数据进行的传递次数,请阅读 Scala 中的函数式编程.
的第 5 章object LazyZipTest extends App{
val incomingUrl = List("users", "profile", "12345", "extra").view
val urlToCompare = List("users", "profile", ":id").view
val list1 = incomingUrl.map(Some(_))
val list2 = urlToCompare.map(Some(_))
val zipped = list1.zipAll(list2, None, None)
println(zipped)
}
第二种实现使用自定义折叠仅遍历列表一次。由于我们要追加到数据结构的尾部,所以我们要使用 IndexedSeq,而不是 List。无论如何,您应该很少使用 List。否则,如果您要从 List 转换为 IndexedSeq,您实际上是在对数据进行一次额外的传递,在这种情况下,您最好不要打扰,只需使用我已经在另一个答案中写过的简单实现。
这是自定义折叠。
object FoldTest extends App{
val incomingUrl = List("users", "profile", "12345", "extra").toIndexedSeq
val urlToCompare = List("users", "profile", ":id").toIndexedSeq
def onePassZip[T, U](l1: IndexedSeq[T], l2: IndexedSeq[U]): IndexedSeq[(Option[T], Option[U])] = {
val folded = l1.foldLeft((l2, IndexedSeq[(Option[T], Option[U])]())) { (acc, e) =>
acc._1 match {
case x +: xs => (xs, acc._2 :+ (Some(e), Some(x)))
case IndexedSeq() => (IndexedSeq(), acc._2 :+ (Some(e), None))
}
}
folded._2 ++ folded._1.map(x => (None, Some(x)))
}
println(onePassZip(incomingUrl, urlToCompare))
println(onePassZip(urlToCompare, incomingUrl))
}
大家有什么问题,我可以在评论区一一解答。