【问题标题】:Recursion Vs. Tail Recursion递归与。尾递归
【发布时间】:2014-03-31 14:22:56
【问题描述】:

我对函数式编程很陌生,尤其是下面使用的 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)
)

【问题讨论】:

    标签: recursion functional-programming tail


    【解决方案1】:

    只是想制作一个使用字符列表的 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)))))
    

    停在那里没用。由于这现在已成为列表迭代,我们可以使用更高阶的过程。通常我们想要fold-leftfoldlSRFI-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 中始终是尾递归的。

    【讨论】:

    • 您可能需要补充一点,fold 的 SRFI/1 参考实现确实是尾递归的(请参阅srfi.schemers.org/srfi-1/srfi-1-reference.scm)。
    • @uselpa 左fold 将永远存在。许多实现提供了他们自己的 SRFI-1,它一直是尾递归的。 TRMCO-ed map 的性能比参考实现要好得多。
    • 为什么要停在fold?您可以使用 SRFI 13 中的 string-fold,并跳过 string->list 步骤。 ;-)
    • @ChrisJester-Young string-fold 不接受多个字符串参数,因此您不能以同样的方式使用它。
    • @Sylwester 快,提交错误报告! (但说真的,我可以理解为什么有多个字符串很麻烦,因为还需要能够选择性地提供开始/结束参数。不过,很糟糕。)
    【解决方案2】:

    下面是累加器的样子:

    (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 开始。我们还有一个 index(也从 0 开始)来跟踪要抓取的字符串中的哪个位置。基本情况是,当我们到达任一字符串的末尾时,返回到目前为止累积的 score

    【讨论】:

    • 非常感谢:) 我想我现在掌握了这个概念。您的代码非常有意义。再次感谢:)
    猜你喜欢
    • 2011-03-03
    • 1970-01-01
    • 1970-01-01
    • 2019-10-30
    • 2016-03-21
    • 2017-11-11
    • 1970-01-01
    • 2018-03-17
    • 1970-01-01
    相关资源
    最近更新 更多