插入无处不在的程序

时间:2013-11-21 15:58:42

标签: scheme lisp racket

我正在尝试编写一个带有符号和列表的过程,并在给定列表中的每个可能位置插入符号(从而生成列表列表)。我编写了以下定义,我必须实现这些定义:

1

(define (insert-at pos elmt lst)
 (if (empty? lst) (list elmt)
 (if (= 1 pos)
  (cons elmt lst)
  (cons (first lst) 
        (insert-at (- pos 1) elmt (rest lst))))))

2

(define (generate-all-pos start end)
 (if (= start end)
  (list end)
  (cons start (generate-all-pos (+ start 1) end))))

1在列表(数字),符号和列表本身中占据一个位置,并在要求的位置插入符号。 2取一个起始位置和一个目标位置(数字),并创建一个带有从开始到目标的数字的排序列表。 到目前为止,我已经得到了这个:

(define (insert-everywhere sym los)
 (cond
  [(empty? los) (list sym)]
   [else (cons (insert-at (first (generate-all-pos (first los)
    (first (foldl cons empty los)))) sym los) (insert-everywhere sym (rest los)))
           ]
         )
       )

结果是

> (insert-everywhere 'r '(1 2 3))
(list (list 'r 1 2 3) (list 2 'r 3) (list 3 'r) 'r)

所以我实际上设法移动'r'。我对保留列表中的前面成员感到很困惑。也许我错过了一些非常简单的东西,但是我已经盯着代码看了很长时间,这是迄今为止我做过的最干净的结果。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:4)

Óscar López's answer显示了如何根据您已定义的过程执行此操作。我想指出一种方法来执行此操作,以减少输入列表。它使用一个名为revappend的辅助函数(我从Common Lisp的revappend中取名)。 revappend采用列表和尾部,并有效地返回(append (reverse list) tail)所需的相同内容。

(define (revappend list tail)
  (if (null? list)
      tail
      (revappend (rest list)
                 (list* (first list) tail))))

> (revappend '(3 2 1) '(4 5 6))
'(1 2 3 4 5 6)

我们对这样一个函数感兴趣的原因是,当我们递减输入列表时,我们可以建立一个我们已经看过的元素列表,但它的顺序是相反的。也就是说,当我们走下(1 2 3 4 5)时,很容易:{/ p>

rhead       tail        (revappend rhead (list* item tail))
----------- ----------- -----------------------------------
         () (1 2 3 4 5) (r 1 2 3 4 5)
        (1) (2 3 4 5)   (1 r 2 3 4 5)
      (2 1) (3 4 5)     (1 2 r 3 4 5)
    (3 2 1) (4 5)       (1 2 3 r 4 5)
  (4 3 2 1) (5)         (1 2 3 4 r 5)
(5 4 3 2 1) ()          (1 2 3 4 5 r)

在每种情况下,(revappend rhead (list* item tail))都会返回在其中一个位置插入item的列表。因此,如果我们以相反的顺序构建insert-everywhere列表,我们就可以用rheadtail以及revappend来定义results,并reverse 1}}它在循环结束时。

(define (insert-everywhere item list)
  (let ie ((tail list)
           (rhead '())
           (results '()))
    (if (null? tail)
        (reverse (list* (revappend rhead (list* item tail)) results))
        (ie (rest tail)
            (list* (first tail) rhead)
            (list* (revappend rhead (list* item tail)) results))))) 

(insert-everywhere 'r '(1 2 3))
;=> '((r 1 2 3) (1 r 2 3) (1 2 r 3) (1 2 3 r))

有趣的是,子列表都具有相同的尾部结构。也就是说,子列表共享结构,如下图“图”所示。

;=> '((r 1 2 3) (1 r 2 3) (1 2 r 3) (1 2 3 r))
;        -----       +++         o   
;          +++         o
;            o

答案 1 :(得分:1)

insert-everywhere程序过于复杂,一个简单的map就可以解决问题。试试这个:

(define (insert-everywhere sym los)
  (map (lambda (i)
         (insert-at i sym los))
       (generate-all-pos 1 (add1 (length los)))))

另请注意,在Racket中存在一个名为range的过程,因此您无需实现自己的generate-all-pos

(define (insert-everywhere sym los)
  (map (lambda (i)
         (insert-at i sym los))
       (range 1 (+ 2 (length los)))))

无论哪种方式,它都按预期工作:

(insert-everywhere 'r '(1 2 3))
=> '((r 1 2 3) (1 r 2 3) (1 2 r 3) (1 2 3 r))