Scala 基于模式匹配的笛卡尔积

Scala 基于模式匹配的笛卡尔积,scala,pattern-matching,cartesian,Scala,Pattern Matching,Cartesian,我是这所大学的学生,正在学习Scala。我有一个练习,就是使用模式匹配,而不是使用列表上的任何操作来生成笛卡尔积 def find[A,B](aList: List[A], bList: List[B]): Set[(A,B)] = { def findHelper[A,B](aList: List[A], bList: List[B], acc: Set[(A,B)]): Set[(A,B)] = { (aList, bList) match { case (head1

我是这所大学的学生,正在学习Scala。我有一个练习,就是使用模式匹配,而不是使用列表上的任何操作来生成笛卡尔积

def find[A,B](aList: List[A], bList: List[B]): Set[(A,B)] = {
  def findHelper[A,B](aList: List[A], bList: List[B], acc: Set[(A,B)]): Set[(A,B)] = {
    (aList, bList) match {
      case (head1 :: tail1, head2::tail2) => {
        findHelper[A, B](tail1, tail2, acc ++ Set((head1, head2)))
      }
      case (List(), head2::tail2) => acc
      case (List(), List()) => acc
    }
  }
  findHelper(aList, bList, Set())
}
println(find(List(1,2,3,4,5), List(7,8,9,10,11)))
结果我只得到了
(1,7)、(2,8)
等。
我当然知道为什么,但我不知道如何将每一对结合起来。当我的第一个列表在
操作后变空时,我不知道该怎么办。

如注释中所述,您只需要遍历一个
列表
,但对于第一个
列表中的每个项目,另一个
列表
都要遍历一次

这里有一个办法

def cartPrd[A,B](aList: List[A], bList: List[B]): Set[(A,B)] = {
  def getAs(as: List[A]): List[(A,B)] = as match {
    case Nil => Nil
    case hd::tl => getBs(hd, bList) ++ getAs(tl)
  }
  def getBs(a: A, bs: List[B]): List[(A,B)] = bs match {
    case Nil => Nil
    case hd::tl => (a,hd) :: getBs(a,tl)
  }
  getAs(aList).toSet
}

cartPrd(List(1,2,1), List('A','B','B'))
//res0: Set[(Int, Char)] = Set((1,A), (1,B), (2,A), (2,B))

用一个简单的
进行理解就容易多了。

如注释中所述,您只需遍历一个
列表
,但对于第一个
列表中的每个项目,另一个
列表
只需遍历一次

这里有一个办法

def cartPrd[A,B](aList: List[A], bList: List[B]): Set[(A,B)] = {
  def getAs(as: List[A]): List[(A,B)] = as match {
    case Nil => Nil
    case hd::tl => getBs(hd, bList) ++ getAs(tl)
  }
  def getBs(a: A, bs: List[B]): List[(A,B)] = bs match {
    case Nil => Nil
    case hd::tl => (a,hd) :: getBs(a,tl)
  }
  getAs(aList).toSet
}

cartPrd(List(1,2,1), List('A','B','B'))
//res0: Set[(Int, Char)] = Set((1,A), (1,B), (2,A), (2,B))

使用一个简单的
来理解
就容易多了。

如注释中所述,问题是您同时迭代两个列表,而您需要为第一个列表的每个元素迭代第二个列表一次

def cartesianProduct[A,B](as:List[A],bs:List[B]):Set[(A,B)]={
@注释.tailrec
def循环(remainingAs:List[A],remainingBs:List[B],acc:Set[(A,B)]):Set[(A,B)]=
(剩余的,剩余的)匹配{
案例(剩余为@(a::uuu),b::tailB)=>
循环(剩余a,剩余b=tailB,acc+(a->b))
案例(tailA,Nil)=>
回路(剩余A=尾A,剩余B=尾B,附件)
案例(无,无)=>
行政协调会
}
循环(remainingAs=as,remainingBs=bs,acc=Set.empty)
}
那条线是什么意思?“case(resmainingas@(a:),b::tailB)”我的意思是,“@”和(a:)做什么

语法
case foo@bar
意味着如果模式匹配的内容与模式
bar
匹配,则将其分配给新变量
foo

因此,在这种情况下,我的意思是,如果as列表不是空的(即是一个cons
),那么将其头部作为一个新变量
a
,并将整个列表作为一个新变量
保留。注意,在这种情况下,根本不需要它,因为我可以使用前面的
remaingas
,我们在上面进行模式匹配,它也包含整个列表;我个人只想定义我将在
case
部分中使用的所有变量,但是您可以只使用
case((a::,b::tailB)
,代码将按照预期编译和工作

您可能做了我需要的:是否保留了as和as不同的值,您只需将完整列表保留在as/bs值中,当它变为空时,您只需再次使用完整列表?例如这里:“case(::tailA,Nil)=>loop(remainingAs=tailA,remainingBs=bs,acc)”

我不完全确定我是否理解你所说的,但你是正确的,我会跟踪原始的第二个列表,以便当我用尽它时,我可以从头开始

因此,正如您所看到的,该代码有三种情况,大致如下所示:

  • 虽然第一个列表不是空的,但是取它的头
  • 然后通过取第二个列表的头部并将两个头部对添加到集合中来迭代第二个列表,并使用第二个列表的尾部继续该过程
  • 当到达第二个列表的尾部时,请再次从第一个列表的尾部开始,并将第二个列表重新启动为其原始形式
  • 继续此过程,直到第一个列表为空,此时返回当前累加器
  • 注意:我个人认为使用两个递归函数更容易理解这个版本。因为这看起来更像是两个循环,第二个循环嵌套在第一个循环中,这是在命令式语言中要做的


    其他解决办法包括:

    两个递归函数:

    def cartesianProduct[A,B](as:List[A],bs:List[B]):Set[(A,B)]={
    @注释.tailrec
    def外部循环(剩余:列表[A],附件:集合[(A,B)]):集合[(A,B)]=
    剩余比赛{
    案例a::tail=>
    @注释.tailrec
    def内部循环(剩余:列表[B],附件:集合[(A,B)]):集合[(A,B)]=
    剩余比赛{
    案例b::tail=>
    内循环(剩余=尾部,acc+(a->b))
    案例无=>
    行政协调会
    }
    val newAcc=内部循环(剩余=bs,acc)
    外侧环(剩余=尾部,新ACC)
    案例无=>
    行政协调会
    }
    outerLoop(剩余=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
    }
    }.托塞特
    


    您可以看到在中运行的代码。

    如注释中所述,问题是您同时迭代两个列表,而您需要为第一个列表的每个元素迭代第二个列表一次

    def cartesianProduct[A,B](as:List[A],bs:List[B]):Set[(A,B)]={
    @注释.tailrec
    def循环(remainingAs:List[A],remainingBs:List[B],acc:Set[(A,B)]):Set[(A,B)]=
    (剩余的,剩余的)匹配{
    案例(剩余为@(a::uuu),b::tailB)=>
    循环(剩余a,剩余b=tailB,acc+(a->b))
    案例(tailA,Nil)=>
    回路(剩余A=尾A,剩余B=尾B,附件)
    案例(无,无)=>
    行政协调会
    }
    循环(remainingAs=as,remainingBs=bs,acc=Set.empty)
    }
    
    那条线是什么意思?“case(resmainingas@(a:),b::tailB)”我的意思是,“@”和(a:)做什么

    语法
    case foo@bar
    表示