方案递归

时间:2018-12-07 15:49:29

标签: scheme racket

Deos谁都知道,我如何通过将函数插入某个地方来使此函式递归?除了附录,make-pair(list)和reverse之外,我不允许对列表使用已实现的功能。

(: split-list ((list-of %a) -> (tuple-of (list-of %a) (list-of %a))))  
(check-expect (split-list (list 1 2)) (make-tuple (list 1) (list 2)))
(check-expect (split-list (list 1 2 3 4)) (make-tuple (list 1 3) (list 2 4)))
(check-expect (split-list (list 1 2 3)) (make-tuple (list 1 3) (list 2)))
(check-expect (split-list (list 1 2 3 4 5)) (make-tuple (list 1 3 5) (list 2 4)))
(check-expect (split-list (list 1 2 3 4 5 6)) (make-tuple (list 1 3 5) (list 2 4 6)))
(define split-list
  (lambda (x)
    (match x
      (empty empty)
      ((make-pair a empty) (make-tuple a empty))
      ((make-pair a (make-pair b empty)) (make-tuple (list a) (list b)))
      ((make-pair a (make-pair b c))  (make-tuple (list a (first c)) (list b (first(rest c))))))))

元组代码:

(define-record-procedures-parametric tuple tuple-of
  make-tuple
  tuple?
  (first-tuple
   rest-tuple))

4 个答案:

答案 0 :(得分:2)

这是一种可以使用matchnamed let(以下称为loop)进行修复的方法。

(define (split xs)
  (let loop ((xs xs)       ;; the list, initialized with our input
             (l empty)     ;; "left" accumulator, initialized with an empty list
             (r empty))    ;; "right" accumulator, initialized with an empty list
    (match xs
      ((list a b rest ...) ;; at least two elements
       (loop rest
             (cons a l)
             (cons b r)))
      ((cons a empty)      ;; one element
       (loop empty
             (cons a l)
             r))
      (else                ;; zero elements
       (list (reverse l)
             (reverse r))))))

上面,我们使用loop建立 left right 列表,然后使用reverse返回最终答案。如果我们以相反的顺序构建答案,我们可以避免不得不反转答案!这里使用的技术称为continuation passing style

(define (split xs (then list))
  (match xs
    ((list a b rest ...)             ;; at least two elements
     (split rest
            (λ (l r)
              (then (cons a l)
                    (cons b r)))))
    ((cons a empty)                  ;; only one element
     (then (list a) empty))
    (else                            ;; zero elements
     (then empty empty))))

两个实现都符合规范。

(split '())
;; => '(() ())

(split '(1)) 
;; => '((1) ())

(split '(1 2 3 4 5 6 7))
;; => '((1 3 5 7) (2 4 6))

将结果分组到list中是一种直观的默认设置,但是您可能仍打算对单独的部分进行处理

(define my-list '(1 2 3 4 5 6 7))

(let* ((result (split my-list))  ;; split the list into parts
       (l (car result))          ;; get the "left" part
       (r (cadr result)))        ;; get the "right" part
  (printf "odds: ~a, evens: ~a~n" l r))
;; odds: (1 3 5 7), evens: (2 4 6)

以上,连续传递样式使我们可以对返回结果进行独特的控制。可以在呼叫站点使用第二个参数来配置继续。

(split '(1 2 3 4 5 6 7) list)  ;; same as default
;; '((1 3 5 7) (2 4 6))

(split '(1 2 3 4 5 6 7) cons)
;; '((1 3 5 7) 2 4 6)

(split '(1 2 3 4 5 6 7)
       (λ (l r)
         (printf "odds: ~a, evens: ~a~n" l r)))
;; odds: (1 3 5 7), evens: (2 4 6)

(split '(1 2 3 4 5 6 7)
       (curry printf "odds: ~a, evens: ~a~n"))
;; odds: (1 3 5 7), evens: (2 4 6)

奥斯卡使用辅助助手功能的答案或本文中使用loop的第一个实现都是实用且惯用的程序。延续传球风格是一种很好的学术练习,但我在这里仅演示了它,因为它显示了如何完成两个复杂的任务:

  1. 建立输出列表而不必反转它
  2. 返回多个值

答案 1 :(得分:1)

我无权访问您使用的make-pairmake-tuple的定义。我可以从Scheme列表的角度考虑一种递归算法,应该很容易将其适应您的要求,只需使用make-tuple代替listmake-pair代替{{ 1}}并进行必要的调整:

cons

例如:

(define (split lst l1 l2)
  (cond ((empty? lst) ; end of list with even number of elements
         (list (reverse l1) (reverse l2))) ; return solution
        ((empty? (rest lst)) ; end of list with odd number of elements
         (list (reverse (cons (first lst) l1)) (reverse l2))) ; return solution
        (else ; advance two elements at a time, build two separate lists
         (split (rest (rest lst)) (cons (first lst) l1) (cons (second lst) l2)))))

(define (split-list lst)
  ; call helper procedure with initial values
  (split lst '() '()))

答案 2 :(得分:0)

split是一种de-interleave函数。在许多其他语言中,split命名用于创建保留实际顺序的列表/序列的子列表/子序列的函数。这就是为什么我不喜欢将此函数命名为split的原因,因为它以某种方式更改了元素的顺序。

尾呼叫递归解决方案

(define de-interleave (l (acc '(() ())))
  (cond ((null? l) (map reverse acc)) ; reverse each inner list
        ((= (length l) 1)             
         (de-interleave '() (list (cons (first l) (first acc))
                                  (second acc))))
        (else                         
         (de-interleave (cddr l) (list (cons (first l) (first acc)) 
                                       (cons (second l) (second acc)))))))

答案 3 :(得分:0)

您似乎正在使用模块deinprogramm / DMdA-vanilla。 最简单的方法是匹配列表的当前状态,然后与其余列表再次调用:

(define split-list
 (lambda (x)
  (match x
   ;the result should always be a tuple
   (empty (make-tuple empty empty))
   ((list a) (make-tuple (list a) empty)) 
   ((list a b) (make-tuple (list a) (list b)))
   ;call split-list with the remaining elements, then insert the first two elements to each list in the tuple
   ((make-pair a (make-pair b c))
    ((lambda (t) 
     (make-tuple (make-pair a (first-tuple t))
                 (make-pair b (rest-tuple t))))
     (split-list c))))))
相关问题