正如 cmets 中所述,问题在于您同时迭代两个列表,而您需要为第一个列表的每个元素迭代第二个列表一次。
def cartesianProduct[A, B](as: List[A], bs: List[B]): Set[(A, B)] = {
@annotation.tailrec
def loop(remainingAs: List[A], remainingBs: List[B], acc: Set[(A, B)]): Set[(A, B)] =
(remainingAs, remainingBs) match {
case (remainingAs @ (a :: _), b :: tailB) =>
loop(remainingAs, remainingBs = tailB, acc + (a -> b))
case (_ :: tailA, Nil) =>
loop(remainingAs = tailA, remainingBs = bs, acc)
case (Nil, _) =>
acc
}
loop(remainingAs = as, remainingBs = bs, acc = Set.empty)
}
那条线是什么意思? " case (remainingAs @ (a :: ), b :: tailB) " 我的意思是,"@" 和 (a :: _) 是做什么的?
语法case foo @ bar 表示如果您的模式匹配与模式bar 匹配,则将其分配给新变量foo。
所以,在这种情况下,我是说如果 as 的列表不为空 (即是一个缺点 ::) 然后将其头部作为一个新变量 a 和整个列为新变量remainingAs。请注意,在这种情况下,根本不需要它,因为我可以使用之前的 remainingAs 进行模式匹配,它还包含整个列表;我个人只是喜欢在case 部分定义我将要使用的所有变量,但您可以只使用case ((a :: _), b :: tailB),代码将按预期编译和工作。
你可能做了我需要的事情:remainingAs 和 as 不同的值,你只需将完整的 List 保存在 as/bs 值中,当它为空时,你只是再次使用完整的?例如这里:“case (::tailA, Nil) => loop(remainingAs = tailA, remainingBs = bs, acc)”
我不完全确定我是否理解你在说什么,但你是对的,我会跟踪原始的第二个列表,以便当我用完它时,我可以从头开始。
所以,正如你所看到的,代码有三种情况,可以或多或少地阅读为:
- 虽然第一个列表不为空,但请抓住它的头。
- 然后通过获取第二个列表的头部并将两个头部的对添加到集合中来迭代第二个列表,并使用第二个列表的尾部继续该过程。
- 当您到达第二个列表的尾部时,然后从第一个列表的尾部重新开始,并将第二个列表重新启动为其原始形式。
- 继续该过程直到第一个列表为空,此时返回当前累加器。
注意:我个人认为有两个递归函数的版本更容易理解。因为这看起来更像是两个循环,第二个循环嵌套在第一个循环中,这就是您在命令式语言中所做的。
其他解决方案包括:
两个递归函数:
def cartesianProduct[A, B](as: List[A], bs: List[B]): Set[(A, B)] = {
@annotation.tailrec
def outerLoop(remaining: List[A], acc: Set[(A, B)]): Set[(A, B)] =
remaining match {
case a :: tail =>
@annotation.tailrec
def innerLoop(remaining: List[B], acc: Set[(A, B)]): Set[(A, B)] =
remaining match {
case b :: tail =>
innerLoop(remaining = tail, acc + (a -> b))
case Nil =>
acc
}
val newAcc = innerLoop(remaining = bs, acc)
outerLoop(remaining = tail, newAcc)
case Nil =>
acc
}
outerLoop(remaining = as, acc = Set.empty)
}
或高阶函数:
(您也可以使用for 语法编写此代码)
def cartesianProduct[A, B](as: List[A], bs: List[B]): Set[(A, B)] =
as.iterator.flatMap { a =>
bs.iterator.map { b =>
a -> b
}
}.toSet
可以看到Scastie中运行的代码。