当前位置: 首页 > 知识库问答 >
问题:

递归与尾递归

孙才捷
2023-03-14

我对函数式编程很陌生,尤其是下面使用的Scheme。我正在尝试使以下函数是递归的,尾递归的。基本上,该函数的作用是对两个字符串的对齐方式进行评分。当给定两个字符串作为输入时,它会比较每个“列”字符,并根据在称为 scorer 的函数中实现的评分方案(由下面的代码中的函数调用)来累积该对齐的分数。

我有一个想法,用一个帮助函数来累积分数,但我不太确定如何去做,因此我该如何让下面的函数尾递归呢?

(define (alignment-score string_one string_two)
  (if (and (not (= (string-length string_one) 0))
           (not (=(string-length string_two) 0)))
      (+ (scorer (string-ref string_one 0)
                 (string-ref string_two 0))
         (alignment-score-not-tail
          (substring string_one 1 (string-length string_one))
          (substring string_two 1 (string-length string_two))
          )
       )
    0)
)

共有2个答案

楮法
2023-03-14

以下是使用累加器时的外观:

(define (alignment-score s1 s2)
  (define min-length (min (string-length s1) (string-length s2)))
  (let loop ((score 0)
             (index 0))
    (if (= index min-length)
        score
        (loop (+ score (scorer (string-ref s1 index)
                               (string-ref s2 index)))
              (+ index 1)))))

在这种情况下,score是累加器,它从0开始。我们还有一个索引(也从0开始),它跟踪要抓取的字符串中的哪个位置。当我们到达任一字符串的末尾时,基本情况是返回迄今为止累积的得分。

池阳伯
2023-03-14

我只是想用字符列表对Chris的答案做一个修改:

(define (alignment-score s1 s2)
  (let loop ((score 0)
             (l1 (string->list s1))
             (l2 (string->list s2)))
    (if (or (null? l1) (null? l2))
        score
        (loop (+ score (scorer (car l1)
                               (car l2)))
              (cdr l1)
              (cdr l2)))))

停在那里没有用。因为这已经成为列表迭代,我们可以使用更高阶程序。通常,我们需要一个< code>fold-left或< code>foldl,SRFI-1 fold就是这样一个实现,它不要求列表长度相同:

; (import (scheme) (only (srfi :1) fold)) ; r7rs    
; (import (rnrs) (only (srfi :1) fold))   ; r6rs    
; (require srfi/1)                        ; racket

(define (alignment-score s1 s2)
  (fold (lambda (a b acc)
          (+ acc (scorer a b)))
        0
        (string->list s1)
        (string->list s2)))

如果你在累加,并且顺序无关紧要,总是选择左折叠,因为在Scheme中它总是尾部递归的。

 类似资料:
  • 尾递归优化 recur 尾递归优化是函数式编程不能缺少的一个性能优化方案. 没有尾递归, 常有的递归调用也会形成很深的调用栈消耗内存. cljs 和 Clojure 类似, 都需要通过声明 recur 进行优化. 最终代码会被编译为 white 结构的 js 代码,从而提高性能. (defn factorial [acc n] (if (<= n 1) acc (recur (* ac

  • 我有一个关于如何将“递归”转换为“尾部递归”的问题。 这不是家庭作业,只是在我试图润色算法书籍中的递归定理时出现的一个问题。 我熟悉使用递归的两个典型示例(阶乘和斐波那契序列),也知道如何以递归方式和尾部递归方式实现它们。 我的代码如下(我使用Perl只是为了使其简单,但可以轻松地转换为C/Java/C)。 运行代码时,输出如下: 递归函数在返回之前使用不同的参数调用自己两次。我尝试了几种方法将其

  • 我有一个家庭作业问题,它给出了一个递归函数,我必须使用尾部递归来实现它。函数为f(0)=1 f(n)=1 2*f(n-1) 我并不擅长尾部递归,我试着查找示例,但我发现的都是没有斐波那契序列的示例,这没有多大帮助。 我真正拥有的是 我知道尾递归基本上每次调用都计算函数,我只是不知道如何实现它。 编辑:我打了一个错字f(n)应该是1 2*f(n-1)

  • 最初,我发布了一个问题“理解尾部递归向量- Q2)尾递归,这让我很难理解。我理解他们为什么需要尾递归,基本上他们用它来避免迭代,所以他们使用helper作为中间例程...所以他们可以避免将每次迭代放入堆栈...类似这样的东西。和letrec/lambda表达式,如下所示: 第Q2-2行:为什么这是“局部递归”“局部”对我来说是递归的中间例程。。。在这里中间意味着我的理解。。 [我的困惑]尾递归是不

  • 我想知道是否有一些通用方法可以用foo(…)转换“正常”递归foo(…) 作为尾部递归的最后一个调用。 例如(scala): 函数语言将递归函数转换为等价尾部调用的一般解决方案: 一种简单的方法是将非尾部递归函数包装在蹦床单子中。 所以pascal函数不再是递归函数。然而,蹦床单子是需要完成的计算的嵌套结构。最后,是一个尾递归函数,它遍历树状结构,解释它,最后在基本情况下返回值。 Rúnar Bj

  • 假设我编写这样的代码: 我如何让Kotlin优化这些相互递归的函数,以便在不抛出StackOverflower错误的情况下运行main?tailrec关键字适用于单函数递归,但没有更复杂的功能。我还看到一个警告,在使用关键字tailrec的地方没有找到尾部调用。也许这对编译器来说太难了?