Scala递归计算给定谓词的元素

时间:2017-10-06 08:25:25

标签: scala recursion

我有这个迭代函数,它计算列表中布尔值的数量。

def countBoolIter[A](test: A=>Boolean, a: List[A]) = {
    var count = 0
    for(elem <- a){
        if(test(elem)) count += 1
    }
    count
}

传入的第一个参数是isBool函数:

def isBool(i: Any) = i match {
    case _: Boolean => true
    case _    => false
}

调用函数如下所示:

countBoolIter(isBool, List(1, true, 3, true, false, "hi"))
// Output: 3

现在,我尝试将其转换为尾递归函数,如下所示:

def countBoolRec[A](test: A=>Boolean, a: List[A], acc: Int = 0): Int = a match {
    case Nil => acc
    case h :: t if(test(h)) => countBoolRec(test, a, acc+1)
    case h :: t => countBoolRec(test, a, acc)
}

但是,我收到运行时错误,因为该函数没有返回任何内容;没有错误被抛出。我认为它陷入无限循环,这就是为什么没有返回的原因。

问题:我应该如何解决我尝试的递归实施?

2 个答案:

答案 0 :(得分:1)

函数countBoolRec中存在错误:

    @tailrec
    def countBoolRec[A](test: A=>Boolean, a: List[A], acc: Int = 0): Int = a match {
        case Nil => acc
        case h :: t if(test(h)) => countBoolRec(test, t, acc+1)
        case h :: t => countBoolRec(test, t, acc)
    }

在递归调用中,使用t作为参数,不再使用a。如果没有,基本上,你处于无限循环中。

另外,最好使用@tailrec注释来确保实现是“尾递归”。

答案 1 :(得分:1)

您反复使用与输入相同的列表进行递归。

考虑a.head通过测试的情况:

countBoolRec(test,a,0) countBoolRec(test,a,1) countBoolRec(test,a,2) ......等等

@scala.annotation.tailrec // Not that your original code wasn't tail-recursive, but it's a generally good practice to mark code that isn't tail recursive with this annotation
def countBoolRec[A](test: A=>Boolean, a: List[A], acc: Int = 0): Int = a match {
  case Nil => acc
  case h :: t if (test(h)) => countBoolRec(test, t, acc + 1)
  case h :: t => countBoolRec(test, t, acc)
}

虽然你也可以写:

(0 /: a) { (acc, v) => acc + (if (test(v)) 1 else 0) }