Recursion 递归与尾部递归

Recursion 递归与尾部递归,recursion,functional-programming,tail,Recursion,Functional Programming,Tail,我对函数式编程非常陌生,尤其是下面使用的Scheme。我试图使下面的函数是递归的,尾部递归。 基本上,该函数所做的是对两个字符串进行对齐。当给定两个字符串作为输入时,它会比较每个字符的“列”,并根据在下面代码中的函数调用的名为scorer的函数中实现的计分方案,为该对齐累积分数 我有一种想法,就是使用一个helper函数来累积分数,但我不太确定如何做到这一点,因此,我应该如何使尾部下面的函数递归 (define (alignment-score string_one string_two)

我对函数式编程非常陌生,尤其是下面使用的Scheme。我试图使下面的函数是递归的,尾部递归。 基本上,该函数所做的是对两个字符串进行对齐。当给定两个字符串作为输入时,它会比较每个字符的“列”,并根据在下面代码中的函数调用的名为scorer的函数中实现的计分方案,为该对齐累积分数

我有一种想法,就是使用一个helper函数来累积分数,但我不太确定如何做到这一点,因此,我应该如何使尾部下面的函数递归

(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)
)

下面是使用累加器时的情况:

(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开始),它跟踪要获取的字符串中的哪个位置。当我们到达任意一个字符串的末尾时,基本情况是返回到目前为止累积的
分数。

只是想用字符列表来替代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)))))
停在那里没有用。由于这现在已经成为列表迭代,我们可以使用更高阶的过程。通常,我们需要一个
左折
左折
,并且是不要求列表长度相同的实现:

; (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)))

如果累加,且顺序无关紧要,请始终选择左折叠,因为它在方案中始终是尾部递归的。

非常感谢:)我想我现在掌握了这个概念。你的代码非常有意义。再次感谢:)您可能需要补充的是,
fold
的SRFI/1参考实现确实是尾部递归的(请参阅)。@uselpa左
fold
始终是。许多实现都提供了自己的SRFI-1,它始终是尾部递归的。
map
的性能比参考实现要好得多。为什么要停在
fold
?您可以使用SRFI 13中的
字符串折叠
,并跳过
字符串->列表
步骤@ChrisJester Young
string fold
不接受多个字符串参数,因此您不能以相同的方式使用它。@Sylvester Quick,提交一份错误报告!(说真的,我可以理解为什么有多个字符串会很麻烦,因为还需要能够选择性地提供开始/结束参数。不过,还是很糟糕。)