Recursion Scheme中列表的递归-避免过早终止

Recursion Scheme中列表的递归-避免过早终止,recursion,scheme,Recursion,Scheme,我在做HTDP书中的一个问题,你必须创建一个函数来查找列表中的所有排列。本书给出了main函数,问题要求您创建helper函数,该函数将在列表中的任何位置插入一个元素。名为insert_everywhere的助手函数只提供了2个参数 不管我怎么努力,我似乎不能只用两个参数来创建这个函数 这是我的代码: (define (insert_everywhere elt lst) (cond [(empty? lst) empty] [else (append (cons elt l

我在做HTDP书中的一个问题,你必须创建一个函数来查找列表中的所有排列。本书给出了main函数,问题要求您创建helper函数,该函数将在列表中的任何位置插入一个元素。名为
insert_everywhere
的助手函数只提供了2个参数

不管我怎么努力,我似乎不能只用两个参数来创建这个函数

这是我的代码:

(define (insert_everywhere elt lst)
  (cond
    [(empty? lst) empty]
    [else (append (cons elt lst) 
                  (cons (first lst) (insert_everywhere elt (rest lst))))]))
我希望
(在每个地方插入_'a(列表1 2 3))
的输出是
(列表1 2 3 1'a 2 3 1 2'a 3 2 3 3'a)
,但我的列表一直在终止

我已经能够使用第三个参数“position”创建这个函数,在这里我对该参数进行递归,但这会把我的主函数搞砸。是否仍然可以创建只有两个参数的辅助函数?谢谢

您是否尝试过:

(define (insert x index xs)
    (cond ((= index 0) (cons x xs))
          (else (cons (car xs) (insert x (- index 1) (cdr xs))))))

(define (range from to)
    (cond ((> from to) empty)
          (else (cons from (range (+ from 1) to)))))

(define (insert-everywhere x xs)
    (fold-right (lambda (index ys) (append (insert x index xs) ys))
        empty (range 0 (length xs))))
insert
功能允许您在列表中的任意位置插入值:

(insert 'a 0 '(1 2 3)) => (a 1 2 3)
(insert 'a 1 '(1 2 3)) => (1 a 2 3)
(insert 'a 2 '(1 2 3)) => (1 2 a 3)
(insert 'a 3 '(1 2 3)) => (1 2 3 a)
range
函数允许您创建Haskell样式的列表范围:

(range 0 3) => (0 1 2 3)
insert everywhere
函数利用
insert
范围
。很容易理解它是如何工作的。如果您的方案实施不具有
折叠权限
功能(例如mzscheme),则您可以按如下方式定义它:

(define (fold-right f acc xs)
    (cond ((empty? xs) acc)
          (else (f (car xs) (fold-right f acc (cdr xs))))))
(define (insert-everywhere elt lst)
  (for/fold ((res null)) ((i (in-range (add1 (length lst)))))
    (append res (take lst i) (cons elt (drop lst i)))))
顾名思义,
向右折叠
功能从右侧折叠列表。

您是否尝试过:

(define (insert x index xs)
    (cond ((= index 0) (cons x xs))
          (else (cons (car xs) (insert x (- index 1) (cdr xs))))))

(define (range from to)
    (cond ((> from to) empty)
          (else (cons from (range (+ from 1) to)))))

(define (insert-everywhere x xs)
    (fold-right (lambda (index ys) (append (insert x index xs) ys))
        empty (range 0 (length xs))))
insert
功能允许您在列表中的任意位置插入值:

(insert 'a 0 '(1 2 3)) => (a 1 2 3)
(insert 'a 1 '(1 2 3)) => (1 a 2 3)
(insert 'a 2 '(1 2 3)) => (1 2 a 3)
(insert 'a 3 '(1 2 3)) => (1 2 3 a)
range
函数允许您创建Haskell样式的列表范围:

(range 0 3) => (0 1 2 3)
insert everywhere
函数利用
insert
范围
。很容易理解它是如何工作的。如果您的方案实施不具有
折叠权限
功能(例如mzscheme),则您可以按如下方式定义它:

(define (fold-right f acc xs)
    (cond ((empty? xs) acc)
          (else (f (car xs) (fold-right f acc (cdr xs))))))
(define (insert-everywhere elt lst)
  (for/fold ((res null)) ((i (in-range (add1 (length lst)))))
    (append res (take lst i) (cons elt (drop lst i)))))

顾名思义,
向右折叠
函数从右边折叠一个列表。

如何为helper函数创建一个helper函数

(define (insert_everywhere elt lst)
    (define (insert_everywhere_aux elt lst)
      (cons (cons elt lst)
            (if (empty? lst)
                empty
                (map (lambda (x) (cons (first lst) x))
                     (insert_everywhere_aux elt (rest lst))))))
    (apply append (insert_everywhere_aux elt lst)))
我们需要我们的子列表保持独立,以便每个子列表都可以单独添加前缀。如果我们过早地附加所有,我们将失去边界。因此,我们最后只追加一次:

insert a (list 1 2 3) =                             ; step-by-step illustration:
                                            ((a))   ; the base case;
                              ((a/ 3)/    (3/ a))   ; '/' signifies the consing
                 ((a/ 2 3)/  (2/ a 3)   (2/ 3 a))
   ((a/ 1 2 3)/ (1/ a 2 3) (1/ 2 a 3) (1/ 2 3 a))
   ( a  1 2 3    1  a 2 3   1  2 a 3   1  2 3 a )   ; the result
测试:

(insert_everywhere 'a (list 1 2 3))
;Value 19: (a 1 2 3 1 a 2 3 1 2 a 3 1 2 3 a)
顺便说一句,这种内部功能或多或少也可以在图中看到。这表明应该可以将其转换为迭代形式。另一种方法是使用
revappend
。提前反转列表简化了他的解决方案中的流程(现在对应于从右到左直接构建图中的结果行,而不是我的版本中的“按列”):


为helper函数创建一个helper函数怎么样

(define (insert_everywhere elt lst)
    (define (insert_everywhere_aux elt lst)
      (cons (cons elt lst)
            (if (empty? lst)
                empty
                (map (lambda (x) (cons (first lst) x))
                     (insert_everywhere_aux elt (rest lst))))))
    (apply append (insert_everywhere_aux elt lst)))
我们需要我们的子列表保持独立,以便每个子列表都可以单独添加前缀。如果我们过早地附加所有,我们将失去边界。因此,我们最后只追加一次:

insert a (list 1 2 3) =                             ; step-by-step illustration:
                                            ((a))   ; the base case;
                              ((a/ 3)/    (3/ a))   ; '/' signifies the consing
                 ((a/ 2 3)/  (2/ a 3)   (2/ 3 a))
   ((a/ 1 2 3)/ (1/ a 2 3) (1/ 2 a 3) (1/ 2 3 a))
   ( a  1 2 3    1  a 2 3   1  2 a 3   1  2 3 a )   ; the result
测试:

(insert_everywhere 'a (list 1 2 3))
;Value 19: (a 1 2 3 1 a 2 3 1 2 a 3 1 2 3 a)
顺便说一句,这种内部功能或多或少也可以在图中看到。这表明应该可以将其转换为迭代形式。另一种方法是使用
revappend
。提前反转列表简化了他的解决方案中的流程(现在对应于从右到左直接构建图中的结果行,而不是我的版本中的“按列”):


您只需拥有两个列表(头部和尾部)并将元素从一个列表滑动到另一个列表即可:

(define (insert-everywhere elt lst)
  (let loop ((head null) (tail lst))      ; initialize head (empty), tail (lst)
    (append (append head (cons elt tail)) ; insert elt between head and tail
            (if (null? tail)
                null                      ; done
                (loop (append head (list (car tail))) (cdr tail)))))) ; slide


(insert-everywhere 'a (list 1 2 3))
=> '(a 1 2 3 1 a 2 3 1 2 a 3 1 2 3 a)
在《球拍》中,你也可以用一种非常简洁的方式来表达它,如下所示:

(define (fold-right f acc xs)
    (cond ((empty? xs) acc)
          (else (f (car xs) (fold-right f acc (cdr xs))))))
(define (insert-everywhere elt lst)
  (for/fold ((res null)) ((i (in-range (add1 (length lst)))))
    (append res (take lst i) (cons elt (drop lst i)))))

您只需拥有两个列表(头部和尾部)并将元素从一个列表滑动到另一个列表即可:

(define (insert-everywhere elt lst)
  (let loop ((head null) (tail lst))      ; initialize head (empty), tail (lst)
    (append (append head (cons elt tail)) ; insert elt between head and tail
            (if (null? tail)
                null                      ; done
                (loop (append head (list (car tail))) (cdr tail)))))) ; slide


(insert-everywhere 'a (list 1 2 3))
=> '(a 1 2 3 1 a 2 3 1 2 a 3 1 2 3 a)
在《球拍》中,你也可以用一种非常简洁的方式来表达它,如下所示:

(define (fold-right f acc xs)
    (cond ((empty? xs) acc)
          (else (f (car xs) (fold-right f acc (cdr xs))))))
(define (insert-everywhere elt lst)
  (for/fold ((res null)) ((i (in-range (add1 (length lst)))))
    (append res (take lst i) (cons elt (drop lst i)))))

这与我们有很多共同之处。有一个过程在你需要它之前似乎有点奇怪,它非常有用,叫做
revappend
(append'(ab…)(xy…)
返回一个列表
(ab…xy…
),其中包含
(ab…
的元素。由于递归遍历列表时很容易按相反顺序收集列表,因此有时使用
revappend
,它会反转第一个参数,以便
(revappend'(ab…mn)'(xy…)
返回
(nm…ba x y…
revappend
易于高效实施:

(define (revappend list tail)
  (if (null? list)
      tail
      (revappend (rest list)
                 (list* (first list) tail))))
现在,这个
insert everywhere
的直接版本非常简单。这个版本不是尾部递归的,但是它非常简单,并且不做任何不必要的列表复制。我们的想法是沿着
lst
走下去,最终得到以下
rhead
tail

rhead   tail    (revappend rhead (list* item (append tail ...)))
------- ------- ------------------------------------------------
     () (1 2 3) (r 1 2 3 ...)
    (1) (2 3)   (1 r 2 3 ...)
  (2 1) (3)     (1 2 r 3 ...)
(3 2 1) ()      (1 2 3 r ...)
如果将递归调用放在
的位置,则会得到所需的结果:

(define (insert-everywhere item lst)
  (let ie ((rhead '())
           (tail lst))
    (if (null? tail)
        (revappend rhead (list item))
        (revappend rhead
                   (list* item 
                          (append tail
                                  (ie (list* (first tail) rhead)
                                      (rest tail))))))))
现在,这不是尾部递归。如果您想要一个尾部递归(因而是迭代)版本,您必须以稍微向后的方式构造结果,然后在最后反转所有内容。您可以这样做,但它确实意味着列表的一个额外副本(除非您以破坏性方式反转它)


这与我们有很多共同之处。有一个过程在你需要它之前似乎有点奇怪,它非常有用,叫做
revappend
(append'(ab…)(xy…)
返回一个列表
(ab…xy…
),其中包含
(ab…
的元素。由于递归遍历列表时很容易按相反顺序收集列表,因此有时使用
revappend
,它会反转第一个参数,以便
(revappend'(ab…mn)'(xy…)
返回
(nm…ba x y…
revappend
易于高效实施:

(define (revappend list tail)
  (if (null? list)
      tail
      (revappend (rest list)
                 (list* (first list) tail))))
现在,这个
insert everywhere
的直接版本非常简单。这个版本不是