使用递归尾部从Scala的末尾查找第N个元素

时间:2019-01-13 23:35:34

标签: scala recursion

我是Scala的新手,并且尝试在下面了解此程序。特别是我听不懂这行

case _ :: tail        =>
            lastNthR(count - 1,
              if (count > 0) resultList else resultList.tail,
              tail)

让我们说我必须找到最后一个第二个元素6,代码变成lastNthR(2-1=1 ?,因为1> 0应该得到resultList我在这里吗?其他resultlist.tail, tail

我不明白。有人可以向我解释一下。我挠了很久。这是完整的代码:

  def lastNthRecursive[A](n: Int, ls: List[A]): A = {
      def lastNthR(count: Int, resultList: List[A], curList: List[A]): A =
        curList match {
          case Nil if count > 0 => throw new NoSuchElementException
          case Nil              => resultList.head
          case _ :: tail        =>
            lastNthR(count - 1,
              if (count > 0) resultList else resultList.tail,
              tail)
        }

      if (n <= 0) throw new IllegalArgumentException
      else lastNthR(n, ls, ls)
  }

  println(lastNthRecursive(2,List(4,5,6,7)))

3 个答案:

答案 0 :(得分:1)

我不确定这是标题中的错字还是您错过了,但是此方法(顾名思义)从末尾找到第N个元素 < / em> (或以相反的顺序)以1作为最后一个元素(这在编程中是不常见的表示法,通常索引以0开头)。

我认为您搞砸了格式。让我们将此代码重写为等效形式:

if(count > 0) {
 lastNthR(count - 1,
          resultList,
          tail)
}
else {
 lastNthR(count - 1,
          resultList.tail,
          tail)
}

换句话说,在您的代码中,if(count > 0)语句会影响lastNthR调用中第二个参数的计算,而第一个参数(count - 1)和第三个参数({{1} })是“固定的”,这就是在行尾用tail表示的逗号(,)。

从逻辑上讲,此代码分为两个阶段:

  1. if成立的第一阶段,我们将if(count > 0)curList分别减小1,直到count变为0。
  2. 在第二个阶段,count为假时,我们将if(count > 0)curList分别减少1,直到resultList为空。

换句话说,算法说从末尾找到第curList个元素与从头开始找到第n个元素相同。第一阶段找到此length(list) - n值(以该长度的列表表示),然后第二阶段从头开始进行搜索。

您可以重新编写该代码,用两种不同的方法将阶段明确地分开:

length(list) - n

P.S。我不喜欢这样的事实,即例外情况def lastNthRecursive[A](n: Int, ls: List[A]): A = { // stage 1 // note, there is actually a very similar a standard method on a List // but it doesn't fail in case of an overshot (n > length(list)) def dropN(count: Int, curList: List[A]): List[A] = curList match { case _ if (count == 0) => curList // important to be above the Nil case case Nil => throw new IndexOutOfBoundsException(s"$n is more than the list length") case _ :: tail => dropN(count - 1, tail) } // stage 2 def dropAll(longList:List[A], shortList:List[A]):List[A] = shortList match { case Nil => longList case _ => dropAll(longList.tail, shortList.tail) } if (n < 0) throw new IndexOutOfBoundsException(s"$n is less than 0") else { val shortenedList = dropN(n, ls) val resultList = dropAll(ls, shortenedList) resultList.head } } IllegalArgumentException不一致,并且根本没有提供文字描述。在这种情况下,恕我直言还有一个更好的例外-NoSuchElementException

答案 1 :(得分:1)

非常有趣的例子!

它使用列表和自身的副本来知道何时停止通过列表的元素。这是在每次调用lastNthR时运行示例时发生的情况:

count = 2
resultList = List(4, 5, 6, 7)
curList = List(4, 5, 6, 7)

count = 1
resultList = List(4, 5, 6, 7)
curList = List(5, 6, 7)

count = 0
resultList = List(4, 5, 6, 7)
curList = List(6, 7)

count = -1
resultList = List(5, 6, 7)
curList = List(7)

count = -2
resultList = List(6, 7)
curList = List()

如您所见,在每一步中,它会将count减1。尽管count大于零,但它不会影响resultList,顾名思义,curList是我们用来获取最终结果的列表。但是,在每个步骤中,都会删除curList中的第一个元素。该函数执行直到resultList的元素用完为止。但是,从curList中删除了前N个元素(在本例中为2个)之后,我们就开始从curList中删除元素。这样,我们将用尽resultList中的元素,而我们仍然拥有resultList中的最后2个元素。

以一种更加图形化的方式,预期的效果就像我们将count: 2 resultList = 4, 5, 6, 7 curList = 4, 5, 6, 7 的两个位置向右移动一样:

curList

然后,我们开始一个接一个地删除元素,直到用尽count: 1 resultList = 4, 5, 6, 7 curList = 5, 6, 7 count: 0 resultList = 4, 5, 6, 7 curList = 6, 7 中的元素,这就是您指出的块所做的:

count

resultList为零或更低时,我们也开始从count: -1 resultList = 5, 6, 7 curList = 7 count: -2 resultList = 6, 7 curList = 中删除项目:

curList

这样,当我们用完resultList中的项目时,我们仍然还有curList match { // ... case Nil => resultList.head // ... } 中剩下的2个元素,因此我们只需要获取第一个即可。这是通过代码完成的:

DirectoryIndex index.php

RewriteEngine On 
RewriteRule ^$ public/index.php [L]
RewriteRule ^((?!public/).*)$ public/$1 [L,NC]

答案 2 :(得分:1)

从列表中获取第n个元素(已建立索引)的方法:

def get[A] (n: Int, list: List[A]): A = {
   @scala.annotation.tailrec
   def go(acc: Int, li: List[A]): A = {
     if(acc == n) List.head(li)
     else go(acc+1, tail(li))
   }

  if(n > List.length(list) - 1) throw new IllegalArgumentException("indexincompatible with List length");
  go(0, list)
}

要从最后获取第n个元素,请使用从开始算起的索引进行调用。