Scala 在“任意”类型列表的末尾添加一个元素(Int、Double…)

Scala 在“任意”类型列表的末尾添加一个元素(Int、Double…),scala,list,recursion,Scala,List,Recursion,我试图在任何类型列表[任何]的末尾添加一个元素。 我想用一个递归函数来构建它,如果我需要这个元素,我会附加它,当迭代结束时,我的列表就会完成。 在下面的代码中,我有一个列表“l”,如果元素“elem”位于“l”的开头,我将添加它的位置,保存在“index”中,作为列表“ret”的下一个元素。否则,我将检查下一个元素,并且不执行任何操作,我使用'l::support…'只是为了匹配返回类型为List[Any]。当“l”为空或为零时,请给我列表“ret”。 “ret”末尾是包含列表“l”中所有元素“

我试图在任何类型列表[任何]的末尾添加一个元素。 我想用一个递归函数来构建它,如果我需要这个元素,我会附加它,当迭代结束时,我的列表就会完成。 在下面的代码中,我有一个列表“l”,如果元素“elem”位于“l”的开头,我将添加它的位置,保存在“index”中,作为列表“ret”的下一个元素。否则,我将检查下一个元素,并且不执行任何操作,我使用'l::support…'只是为了匹配返回类型为List[Any]。当“l”为空或为零时,请给我列表“ret”。 “ret”末尾是包含列表“l”中所有元素“elem”位置的列表。 这一点非常重要:我正在构建的列表是'ret',实际上它是递归函数的返回

我试过:,+:,:+但是没有用。每次错误都是相同的:value::不是类型参数Any的成员

object find{
    def support[Any](l:List[Any],elem:Any,index:Int,ret:List[Any]):List[Any]={
        if (l==Nil) ret;
        else if(l.head==elem) (l :: support(l.tail,elem,index+1,ret :: (index.asInstanceOf[Any])));
        else (l :: support(l.tail,elem,index+1,ret));       
    }
    def findIndices[Any](l:List[Any],x:Any):List[Any]={//I want to find all the elements "elem" in the list "l"
        val a:List[Any]=support(l,x,0,List[Any]());//I'll build my list using an auxiliary function "support"
        a;
    }
}
object main extends App{
    val l:List[Int]=List(1,2,2,2,2,3,4,5)
    val x:Int=2;
    val f:List[Int]=find.findIndices(l,x)
    println(f)
}
我对所有可行的解决方案都持开放态度,但请首先尝试回答我的问题,并解释您是如何做到这一点的,以及原因。
我正在学习这门语言,我来自C和Java。

我认为您应该更多地了解Scala中的泛型。特别是调用泛型参数Any以隐藏标准类型Any的想法是一个非常糟糕的想法。此外,它还可能有助于了解模式匹配,这是一种强大的工具,可以替代某些if/else if/else语句。我相信您想要的代码如下所示:

object find {
  @tailrec
  def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
    case Nil => ret
    case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
    // _ meas here we ignore head anyway so don't need a variable for that
    case _ :: tail => support(tail, elem, index + 1, ret)
  }

  def findIndices[A](l: List[A], x: A): List[Int] = {
    //I want to find all the elements "elem" in the list "l"
    support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
  }
}
def findIndices(list: List[Any], element: Any): List[Any] = ???
def findIndices[T](list: List[T], element: T): List[Int] = {
  // if (terminating case) return termination
  // else if (check for next index of element) return index plus recursive call
  // else return recursive call
}
def findIndices[T](list: List[T], element: T): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) 0 :: findIndices(list.tail, element)
  else findIndices(list.tail, element)
}
你可以试试这个代码


请注意,我做了一些改进,但还有一些改进是可能的。例如,通常这种支持方法会放在findindice内部,因此外部无法使用。而且,它在列表中的速度非常慢。通常情况下,通过将结果列表附加到开始处,然后在最后反转一次,可以更快地构建结果列表。

我认为您应该更多地了解Scala中的泛型。特别是调用泛型参数Any以隐藏标准类型Any的想法是一个非常糟糕的想法。此外,它还可能有助于了解模式匹配,这是一种强大的工具,可以替代某些if/else if/else语句。我相信您想要的代码如下所示:

object find {
  @tailrec
  def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
    case Nil => ret
    case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
    // _ meas here we ignore head anyway so don't need a variable for that
    case _ :: tail => support(tail, elem, index + 1, ret)
  }

  def findIndices[A](l: List[A], x: A): List[Int] = {
    //I want to find all the elements "elem" in the list "l"
    support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
  }
}
def findIndices(list: List[Any], element: Any): List[Any] = ???
def findIndices[T](list: List[T], element: T): List[Int] = {
  // if (terminating case) return termination
  // else if (check for next index of element) return index plus recursive call
  // else return recursive call
}
def findIndices[T](list: List[T], element: T): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) 0 :: findIndices(list.tail, element)
  else findIndices(list.tail, element)
}
你可以试试这个代码

请注意,我做了一些改进,但还有一些改进是可能的。例如,通常这种支持方法会放在findindice内部,因此外部无法使用。而且,它在列表中的速度非常慢。通常,通过将结果列表附加到起始位置,然后在最末尾反转一次,可以更快地构建结果列表。

TL;DR单线解决方案:

或者使用大小写符号命名变量:

list.zipWithIndex.filter { case(value,index) => value == 2 } map { case(value,index) => index }
或者使用collect方法组合过滤器和映射:

list.zipWithIndex.collect { case (value,index) if value == 2 => index }
递归

如果你真的需要使用递归,有一个简单的方法,也有一个困难的方法,看起来你正试图用困难的方法。在这种情况下,艰难的方式是有道理的,但我会先用简单的方式来帮助你了解我在做什么以及为什么

所以给出了一个清单

val list = List(1,2,2,2,2,3,4,5) // named list instead of l because l looks like 1.
我们需要一个函数findIndices,findIndiceslist,2返回List1,2,3,4

我将从定义findindice开始,如下所示:

object find {
  @tailrec
  def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
    case Nil => ret
    case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
    // _ meas here we ignore head anyway so don't need a variable for that
    case _ :: tail => support(tail, elem, index + 1, ret)
  }

  def findIndices[A](l: List[A], x: A): List[Int] = {
    //I want to find all the elements "elem" in the list "l"
    support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
  }
}
def findIndices(list: List[Any], element: Any): List[Any] = ???
def findIndices[T](list: List[T], element: T): List[Int] = {
  // if (terminating case) return termination
  // else if (check for next index of element) return index plus recursive call
  // else return recursive call
}
def findIndices[T](list: List[T], element: T): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) 0 :: findIndices(list.tail, element)
  else findIndices(list.tail, element)
}
现在有几件事我想马上改变。首先,您的示例使用单一类型的事物列表,因此这看起来是使用参数化类型的绝佳机会

def findIndices[T](list: List[T], element: T): List[Any] = ???
第二件事是,无论列表中是什么,结果索引列表都将是整数,因为索引是整数

def findIndices[T](list: List[T], element: T): List[Int] = ???
现在我已经准备好处理这个方法的主体了。我知道这需要递归,递归函数的一般形式是:

检查终止案例。 处理非终止案件。 所以我的函数看起来是这样的:

object find {
  @tailrec
  def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
    case Nil => ret
    case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
    // _ meas here we ignore head anyway so don't need a variable for that
    case _ :: tail => support(tail, elem, index + 1, ret)
  }

  def findIndices[A](l: List[A], x: A): List[Int] = {
    //I want to find all the elements "elem" in the list "l"
    support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
  }
}
def findIndices(list: List[Any], element: Any): List[Any] = ???
def findIndices[T](list: List[T], element: T): List[Int] = {
  // if (terminating case) return termination
  // else if (check for next index of element) return index plus recursive call
  // else return recursive call
}
def findIndices[T](list: List[T], element: T): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) 0 :: findIndices(list.tail, element)
  else findIndices(list.tail, element)
}
填空给我们提供了如下信息:

object find {
  @tailrec
  def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
    case Nil => ret
    case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
    // _ meas here we ignore head anyway so don't need a variable for that
    case _ :: tail => support(tail, elem, index + 1, ret)
  }

  def findIndices[A](l: List[A], x: A): List[Int] = {
    //I want to find all the elements "elem" in the list "l"
    support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
  }
}
def findIndices(list: List[Any], element: Any): List[Any] = ???
def findIndices[T](list: List[T], element: T): List[Int] = {
  // if (terminating case) return termination
  // else if (check for next index of element) return index plus recursive call
  // else return recursive call
}
def findIndices[T](list: List[T], element: T): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) 0 :: findIndices(list.tail, element)
  else findIndices(list.tail, element)
}
不幸的是,这段代码有一个bug。我们根据不断缩短的列表计算索引,而不是原始列表。我们可以通过跟踪索引的偏移量来解决这一问题,因为我们使用的列表版本越来越短:

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) offset :: findIndices(list.tail, element, offset + 1)
  else findIndices(list.tail, element, offset + 1)
}
此方法按预期工作…仅适用于小列表。对于非常大的列表,我们将得到堆栈溢出。解决此问题的方法是使方法尾部递归,因此程序不需要在每次调用时跟踪堆栈。这就是你在问题中想要做的。我称之为艰难的方式,但一旦你有了非尾部递归函数,将其转换为尾部递归函数实际上是相当简单和机械的

顶级功能参数保持不变:

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = ???
在第一个函数中,我们定义了一个新函数。此新函数将具有与旧函数相同的参数,外加一个累加器。这个累加器将允许我们将算法的中间结果沿堆栈向下传递给下一个递归函数调用,这样程序就不会出现错误 我们需要维护调用堆栈以跟踪每个中间结果

我们的外部函数所做的唯一一件事就是用初始参数调用内部函数

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {

  def findIndicesAcc(list: List[T], element: T, acc: List[Int], offset: Int = 0): List[Int] = {
    // do logic here
  }

  findIndicesAcc(list, element, List.empty[Int])
}
累加器函数内的逻辑与原始函数非常相似。它将简单地利用累加器参数,而不是将中间结果留在堆栈上

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {

  def findIndicesAcc(list: List[T], element: T, acc: List[Int], offset: Int = 0): List[Int] = {
    if (list.isEmpty) acc
    else if (list.head == element) findIndicesAcc(list.tail, element, offset + 1, offset :: acc)
    else findIndicesAcc(list.tail, element, offset + 1, acc)
  }

  findIndicesAcc(list, element, List.empty[Int])
}
此功能按预期工作,但我们可以做一些最终记账和清理项目。首先,我们可以去掉最外层的offset参数,去掉内层offset参数的默认值。其次,我们可以在内部函数中添加@tailrec注释,以确保解决了堆栈溢出问题。第三,如果返回列表中索引的顺序很重要,我们可以对累加器的输出调用reverse

import scala.annotation.tailrec

def findIndices[T](list: List[T], element: T): List[Int] = {

  @tailrec
  def findIndicesAcc(list: List[T], element: T, offset: Int, acc: List[Int]): List[Int] = {
    if (list.isEmpty) acc
    else if (list.head == element) findIndicesAcc(list.tail, element, offset + 1, offset :: acc)
    else findIndicesAcc(list.tail, element, offset + 1, acc)
  }

  findIndicesAcc(list, element, 0, List.empty[Int]).reverse
}
关于尾部递归的更多信息

尾部递归是指递归函数,其中递归调用是函数中发生的最后一件事。有时,你必须仔细观察,看看是否有东西是尾部递归的。例如,代码片段

否则,如果list.head==元素偏移量::findIndiceslist.tail,元素,偏移量+1

不是尾部递归的,因为在返回偏移量后,必须在findIndices的结果前面加上偏移量。如果我们将代码片段的每个操作分解为单独的行,则可以更清楚地说明这一点:

else if (list.head == element) {
  val tail = list.tail      
  val newOffset = offset + 1
  val recursiveResult = findIndices(tail, element, newOffset)
  return offset :: recursiveResult
}
当代码像这样分解时,我们可以看到在递归findIndices调用返回后,还需要做更多的工作

另一方面,代码片段

如果list.head==元素findincesacclist.tail,元素,偏移量+1,偏移量::acc

是尾部递归的。当我们把操作分成不同的行时,我们得到

else if (list.head == element) {
  val tail = list.tail
  val newOffset = offset + 1
  val combinedList = offset :: acc
  return findIndicesAcc(tail, element, newOffset, combinedList)
}
我们可以清楚地看到FindDiceSACC调用是最后发生的事情

在第一种情况下,程序被迫维护整个调用堆栈,因为它需要记住函数每次迭代之前的偏移量值。在linux机器中,我们通常要使用8MB的堆栈。对于很长的列表,我们最终会耗尽堆栈的所有8MB,这会导致堆栈溢出异常

在第二种情况下,所有相关数据都传递给递归函数的下一次迭代。在以前的函数调用中,程序不需要跟踪任何内容。编译器能够检测到这一点,并将代码优化为一个循环。在这种情况下,没有需要维护的调用堆栈,因此没有堆栈溢出异常的风险

最后一个警告是,我尽可能仔细地检查了这段代码,但在编写这段代码时,我没有访问scala编译器的权限,因此我为任何输入错误道歉。

TL;DR单线解决方案:

或者使用大小写符号命名变量:

list.zipWithIndex.filter { case(value,index) => value == 2 } map { case(value,index) => index }
或者使用collect方法组合过滤器和映射:

list.zipWithIndex.collect { case (value,index) if value == 2 => index }
递归

如果你真的需要使用递归,有一个简单的方法,也有一个困难的方法,看起来你正试图用困难的方法。在这种情况下,艰难的方式是有道理的,但我会先用简单的方式来帮助你了解我在做什么以及为什么

所以给出了一个清单

val list = List(1,2,2,2,2,3,4,5) // named list instead of l because l looks like 1.
我们需要一个函数findIndices,findIndiceslist,2返回List1,2,3,4

我将从定义findindice开始,如下所示:

object find {
  @tailrec
  def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
    case Nil => ret
    case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
    // _ meas here we ignore head anyway so don't need a variable for that
    case _ :: tail => support(tail, elem, index + 1, ret)
  }

  def findIndices[A](l: List[A], x: A): List[Int] = {
    //I want to find all the elements "elem" in the list "l"
    support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
  }
}
def findIndices(list: List[Any], element: Any): List[Any] = ???
def findIndices[T](list: List[T], element: T): List[Int] = {
  // if (terminating case) return termination
  // else if (check for next index of element) return index plus recursive call
  // else return recursive call
}
def findIndices[T](list: List[T], element: T): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) 0 :: findIndices(list.tail, element)
  else findIndices(list.tail, element)
}
现在有几件事我想马上改变。首先,您的示例使用单一类型的事物列表,因此这看起来是使用参数化类型的绝佳机会

def findIndices[T](list: List[T], element: T): List[Any] = ???
第二件事是,无论列表中是什么,结果索引列表都将是整数,因为索引是整数

def findIndices[T](list: List[T], element: T): List[Int] = ???
现在我已经准备好处理这个方法的主体了。我知道这需要递归,递归函数的一般形式是:

检查终止案例。 处理非终止案件。 所以我的函数看起来是这样的:

object find {
  @tailrec
  def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
    case Nil => ret
    case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
    // _ meas here we ignore head anyway so don't need a variable for that
    case _ :: tail => support(tail, elem, index + 1, ret)
  }

  def findIndices[A](l: List[A], x: A): List[Int] = {
    //I want to find all the elements "elem" in the list "l"
    support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
  }
}
def findIndices(list: List[Any], element: Any): List[Any] = ???
def findIndices[T](list: List[T], element: T): List[Int] = {
  // if (terminating case) return termination
  // else if (check for next index of element) return index plus recursive call
  // else return recursive call
}
def findIndices[T](list: List[T], element: T): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) 0 :: findIndices(list.tail, element)
  else findIndices(list.tail, element)
}
填空给我们提供了如下信息:

object find {
  @tailrec
  def support[A](l: List[A], elem: A, index: Int, ret: List[Int]): List[Int] = l match {
    case Nil => ret
    case head :: tail if head == elem => support(tail, elem, index + 1, ret :+ index)
    // _ meas here we ignore head anyway so don't need a variable for that
    case _ :: tail => support(tail, elem, index + 1, ret)
  }

  def findIndices[A](l: List[A], x: A): List[Int] = {
    //I want to find all the elements "elem" in the list "l"
    support(l, x, 0, List.empty[Int]) //I'll build my list using an auxiliary function "support"
  }
}
def findIndices(list: List[Any], element: Any): List[Any] = ???
def findIndices[T](list: List[T], element: T): List[Int] = {
  // if (terminating case) return termination
  // else if (check for next index of element) return index plus recursive call
  // else return recursive call
}
def findIndices[T](list: List[T], element: T): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) 0 :: findIndices(list.tail, element)
  else findIndices(list.tail, element)
}
不幸的是,这段代码有一个bug。我们根据不断缩短的列表计算索引,而不是原始列表。我们可以通过跟踪索引的偏移量来解决这一问题,因为我们使用的列表版本越来越短:

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {
  if (list.isEmpty) List.empty[Int]
  else if (list.head == element) offset :: findIndices(list.tail, element, offset + 1)
  else findIndices(list.tail, element, offset + 1)
}
此方法按预期工作…仅适用于小列表。对于非常大的列表,我们将得到堆栈溢出。解决此问题的方法是使方法尾部递归,因此程序不需要在每次调用时跟踪堆栈。这就是你在问题中想要做的。我称之为艰难的方式,但一旦你有了非尾部递归函数,将其转换为尾部递归函数实际上是相当简单和机械的

顶级功能参数保持不变:

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = ???
在第一个函数中,我们定义了一个新函数。此新函数将具有与旧函数相同的参数,外加一个累加器。这个蓄能器将 允许我们将算法的中间结果向下传递到下一个递归函数调用,这样程序就不必维护调用堆栈来跟踪每个中间结果

我们的外部函数所做的唯一一件事就是用初始参数调用内部函数

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {

  def findIndicesAcc(list: List[T], element: T, acc: List[Int], offset: Int = 0): List[Int] = {
    // do logic here
  }

  findIndicesAcc(list, element, List.empty[Int])
}
累加器函数内的逻辑与原始函数非常相似。它将简单地利用累加器参数,而不是将中间结果留在堆栈上

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {

  def findIndicesAcc(list: List[T], element: T, acc: List[Int], offset: Int = 0): List[Int] = {
    if (list.isEmpty) acc
    else if (list.head == element) findIndicesAcc(list.tail, element, offset + 1, offset :: acc)
    else findIndicesAcc(list.tail, element, offset + 1, acc)
  }

  findIndicesAcc(list, element, List.empty[Int])
}
此功能按预期工作,但我们可以做一些最终记账和清理项目。首先,我们可以去掉最外层的offset参数,去掉内层offset参数的默认值。其次,我们可以在内部函数中添加@tailrec注释,以确保解决了堆栈溢出问题。第三,如果返回列表中索引的顺序很重要,我们可以对累加器的输出调用reverse

import scala.annotation.tailrec

def findIndices[T](list: List[T], element: T): List[Int] = {

  @tailrec
  def findIndicesAcc(list: List[T], element: T, offset: Int, acc: List[Int]): List[Int] = {
    if (list.isEmpty) acc
    else if (list.head == element) findIndicesAcc(list.tail, element, offset + 1, offset :: acc)
    else findIndicesAcc(list.tail, element, offset + 1, acc)
  }

  findIndicesAcc(list, element, 0, List.empty[Int]).reverse
}
关于尾部递归的更多信息

尾部递归是指递归函数,其中递归调用是函数中发生的最后一件事。有时,你必须仔细观察,看看是否有东西是尾部递归的。例如,代码片段

否则,如果list.head==元素偏移量::findIndiceslist.tail,元素,偏移量+1

不是尾部递归的,因为在返回偏移量后,必须在findIndices的结果前面加上偏移量。如果我们将代码片段的每个操作分解为单独的行,则可以更清楚地说明这一点:

else if (list.head == element) {
  val tail = list.tail      
  val newOffset = offset + 1
  val recursiveResult = findIndices(tail, element, newOffset)
  return offset :: recursiveResult
}
当代码像这样分解时,我们可以看到在递归findIndices调用返回后,还需要做更多的工作

另一方面,代码片段

如果list.head==元素findincesacclist.tail,元素,偏移量+1,偏移量::acc

是尾部递归的。当我们把操作分成不同的行时,我们得到

else if (list.head == element) {
  val tail = list.tail
  val newOffset = offset + 1
  val combinedList = offset :: acc
  return findIndicesAcc(tail, element, newOffset, combinedList)
}
我们可以清楚地看到FindDiceSACC调用是最后发生的事情

在第一种情况下,程序被迫维护整个调用堆栈,因为它需要记住函数每次迭代之前的偏移量值。在linux机器中,我们通常要使用8MB的堆栈。对于很长的列表,我们最终会耗尽堆栈的所有8MB,这会导致堆栈溢出异常

在第二种情况下,所有相关数据都传递给递归函数的下一次迭代。在以前的函数调用中,程序不需要跟踪任何内容。编译器能够检测到这一点,并将代码优化为一个循环。在这种情况下,没有需要维护的调用堆栈,因此没有堆栈溢出异常的风险

最后一个警告是,我尽可能仔细地检查了这段代码,但在编写这段代码时,我没有访问scala编译器的权限,因此我为任何输入错误道歉。

完全手工制作的解决方案 使用标准高阶函数 后果 解释 请随意提出您想要的问题,我将用答案编辑此部分

然而,我将回答几个我相信你们现在会有的问题:

@annotation.tailrec是什么意思?:在代码中,实际上什么都没有。这是一个编译器注释,它告诉编译器检查给定函数是否是尾部递归的,如果不是,则发出警告-这更像是确保函数不会炸毁堆栈的最佳实践

def findIndices[T](list: List[T], element: T, offset: Int = 0): List[Int] = {

  def findIndicesAcc(list: List[T], element: T, acc: List[Int], offset: Int = 0): List[Int] = {
    if (list.isEmpty) acc
    else if (list.head == element) findIndicesAcc(list.tail, element, offset + 1, offset :: acc)
    else findIndicesAcc(list.tail, element, offset + 1, acc)
  }

  findIndicesAcc(list, element, List.empty[Int])
}
为什么是T而不是Any?:这里的T表示泛型,就像类型占位符所说的,这适用于任何类型。另一方面,任何事物都是一种具体的类型——一切事物的超级类型。它们看起来很相似,但泛型确保不会丢失类型信息。如果您有一个Int列表并将其反转,它将返回一个Int列表,而不是任何Int列表

为什么要反转结果?:列表对于预结束很好,但是对于预结束却很糟糕-通常最好迭代两次,一次用于转换,另一次用于反转,除非像列表的内部实现那样使用突变。。。但是如果你不小心的话,这可能会破坏你的计划

_u.\u 1&u.\u 2是什么意思?:第一个下划线用于匿名函数,它意味着第一个参数(在本例中是唯一的参数)_1&_2是可以看到的方法,因为它们是用点调用的。在Tuple类上,它们访问Tuple的第一个和第二个元素

全手工解决方案 使用标准高阶函数 后果 解释 请随意提出您想要的问题,我将用答案编辑此部分

然而,我将回答几个我相信你们现在会有的问题:

@annotation.tailrec是什么意思?:在代码中,实际上什么都没有。这是一个编译器注释,它告诉编译器检查给定函数是否是尾部递归的,如果不是,则发出警告-这更像是确保函数不会炸毁堆栈的最佳实践。

为什么是T而不是Any?:这里的T表示泛型,就像类型占位符所说的,这适用于任何类型。另一方面,任何事物都是一种具体的类型——一切事物的超级类型。它们看起来很相似,但泛型确保不会丢失类型信息。如果您有一个Int列表并将其反转,它将返回一个Int列表,而不是任何Int列表

为什么要反转结果?:列表对于预结束很好,但是对于预结束却很糟糕-通常最好迭代两次,一次用于转换,另一次用于反转,除非像列表的内部实现那样使用突变。。。但是如果你不小心的话,这可能会破坏你的计划

_u.\u 1&u.\u 2是什么意思?:第一个下划线用于匿名函数,它意味着第一个参数(在本例中是唯一的参数)_1&_2是可以看到的方法,因为它们是用点调用的。在Tuple类上,它们访问Tuple的第一个和第二个元素


我不太明白你的问题。是否只想将元素添加到列表的末尾?但是,为什么要搜索索引?第二,此def支持[Any]并没有达到您认为的效果,因为现在您可以删除[Any]。第三,既然你这样做是为了学习,你想要一个只在列表上使用简单递归的解决方案,或者你对使用标准库的内置方法的解决方案持开放态度,或者两者兼而有之?为什么你不能只使用列表:::值::无需或递归。我对所有的解决方案@LuisMiguelMejíaSuárez都持开放态度。等等,我将更改标题,但是我想在通用列表的末尾添加一个元素。元素是x在列表l中的位置,x可以出现多次,可能l=Lista,x,x,x,b,x我的列表将是ret=1,2,3,5 x在l中的所有位置。我希望这能更好地解释我的问题。@feded ahh我现在理解了你的问题,我添加了一个可能对你有帮助的答案。然而,杰里米先生考虑了写一个尽可能细致的更好的答案!我建议你们两个都考虑一下,感谢杰里米先生,接受他的回答,如果可以的话,放弃投票。我不太明白你的问题。是否只想将元素添加到列表的末尾?但是,为什么要搜索索引?第二,此def支持[Any]并没有达到您认为的效果,因为现在您可以删除[Any]。第三,既然你这样做是为了学习,你想要一个只在列表上使用简单递归的解决方案,或者你对使用标准库的内置方法的解决方案持开放态度,或者两者兼而有之?为什么你不能只使用列表:::值::无需或递归。我对所有的解决方案@LuisMiguelMejíaSuárez都持开放态度。等等,我将更改标题,但是我想在通用列表的末尾添加一个元素。元素是x在列表l中的位置,x可以出现多次,可能l=Lista,x,x,x,b,x我的列表将是ret=1,2,3,5 x在l中的所有位置。我希望这能更好地解释我的问题。@feded ahh我现在理解了你的问题,我添加了一个可能对你有帮助的答案。然而,杰里米先生考虑了写一个尽可能细致的更好的答案!我建议你同时考虑这两个问题,并感谢杰里米先生,接受他的回答,如果可以的话,放弃投票。一开始是def支持[T],但问题太多了,我把T改成了任何一个。然而问题是ret:+索引不起作用。感谢您的回复,我认为这并不能解决问题。@feded如果您认为将泛型从t重命名为Any可以解决问题,那么您就大错特错了。@feded将t重命名为Any不会改变任何东西,除非Any类型没有被隐藏。当你说问题是ret:+index不起作用时,你是指你的代码还是我的代码?在我的代码中,它似乎工作得非常好。@BrianMcCutchon我试过,也许它会有帮助,但是我一秒钟都不相信它。开始时,它是def支持[t],但有太多问题,所以我将t改为任何问题。然而问题是ret:+索引不起作用。感谢您的回复,我认为这并不能解决问题。@feded如果您认为将泛型从t重命名为Any可以解决问题,那么您就大错特错了。@feded将t重命名为Any不会改变任何东西,除非Any类型没有被隐藏。当你说问题是ret:+index不起作用时,你是指你的代码还是我的代码?在我的代码中,它似乎工作得非常好。@BrianMcCutchon我试过了,也许它会有帮助,但是我一点也不相信它。谢谢你Jeremy,这正是我想要的……你的解释太好了,对我来说一切都很清楚。只有一个问题:list.head==元素偏移量::findindicesslist.tail,element,offset+1与第二个方法的区别是什么?如果list.head==元素findIndicesAcclist.tail,element,offset+1,offset::acc?我知道这是一件很重要的事情
考虑到堆栈是如何工作的以及跟踪所有递归迭代的困难,我可以想象,但我不知道它是如何工作的。你能给我解释一下这段话吗?我补充了一点关于尾部递归的解释。谢谢Jeremy,这正是我想要的…你的解释太好了,我都明白了。只有一个问题:list.head==元素偏移量::findindicesslist.tail,element,offset+1与第二个方法的区别是什么?如果list.head==元素findIndicesAcclist.tail,element,offset+1,offset::acc?我知道这与堆栈的工作方式以及跟踪所有递归迭代的困难有关,我想是这样,但我不知道它是如何工作的。你能给我解释一下这段话吗?我补充了一点关于尾部递归的解释。我不明白第4点,1和2是什么意思?你能给我解释一下匿名函数是如何工作的吗?@feded如果你看一下,你会发现它接收一个类型为a到B_a=>B_的参数的函数-因此你需要传递一个函数,一种方法是定义一个新的def并传递它。。。但是,每次创建一个新函数并命名它们都很繁琐,因此Scala允许您使用匿名函数,如x=>x+1,在本例中,我们将参数x命名,但只使用一次即可写入u1。我不明白第4点1&2的含义是什么?你能给我解释一下匿名函数是如何工作的吗?@feded如果你看一下,你会发现它接收一个类型为a到B_a=>B_的参数的函数-因此你需要传递一个函数,一种方法是定义一个新的def并传递它。。。但是,每次创建一个新函数并命名它们都很繁琐,因此Scala允许您使用匿名函数,如x=>x+1,在本例中,我们将参数命名为x,但只使用一次即可写入u1。