在scheme中获取树的有序叶

在scheme中获取树的有序叶,scheme,lisp,racket,sicp,Scheme,Lisp,Racket,Sicp,我正在进行一个练习,以获取scheme中嵌套列表的“叶子”(来自SICP)。以下是练习的输入输出: (define x (list (lis 1 2) (list 3 4))) (fringe x) ; (1 2 3 4) (fringe (list x x)) ; (1 2 3 4 1 2 3 4) 现在,我给出了两个答案:一个是递归的,一个是迭代的。下面是我的两个实现: (define (fr lst) (cond ((null? lst) '()) ((not (p

我正在进行一个练习,以获取scheme中嵌套列表的“叶子”(来自SICP)。以下是练习的输入输出:

(define x (list (lis 1 2) (list 3 4)))
(fringe x)
; (1 2 3 4)

(fringe (list x x))
; (1 2 3 4 1 2 3 4)
现在,我给出了两个答案:一个是递归的,一个是迭代的。下面是我的两个实现:

(define (fr lst)
  (cond ((null? lst) '())
        ((not (pair? (car lst))) (cons (car lst) (fr (cdr lst))))
        (else (append (fr (car lst)) (fr (cdr lst))))))
对我来说,问题是,这个练习花了我很长时间才弄明白,在这过程中,我会不断地捶着头(在写这篇文章的时候,我仍然很难“得到它”)。以下是一些我一直在努力解决的问题,看看是否有任何关于如何在计划中处理这些问题的建议:

  • 我最初认为有两种情况。普通/标量情况和嵌套情况。然而,似乎实际上有三个!这里有普通情况,嵌套情况,然后是空情况——内部列表也有空情况!是否有一个好的通用模式或什么东西来解释空案例?这是经常发生的事情吗
  • 在迭代的情况下,为什么我必须在最后返回
    L
    ?为什么
    (iter-lst)
    不直接返回它(即,如果我删除了
    iter
    函数底部的独立-
    L
  • 最后,是否有一种“更干净”的方法来实现迭代案例?似乎我有太多的代码,可能需要改进

  • 有三种情况的原因是,您正在导入一些与其他语言的标量/向量区别:Scheme没有标量/向量,也没有帮助。相反,列表是一个递归定义的对象:列表要么是空列表,要么是一对某物和一个列表。这意味着有两个区别,而不是一个:一个对象是一对,一个对象是空列表:

    (define (lyst? o)
      (or (null? o)
          (and (pair? o) (lyst? (cdr o)))))
    
    这与向量/标量的区别完全不同。我不知道你是从哪种语言得到这个结果的,但是想想这个数学是如何工作的:向量是在某个标量场上定义的,没有向量也是标量的。但是对于列表,有一个列表不是成对的。不要再考虑向量和标量了:考虑列表、对和空列表是没有帮助的

    迭代版本太可怕了,无法想象:SICP没有引入
    set是有原因的还没有

    首先,它实际上不是迭代的:就像网络上解决这个问题的大多数“迭代”解决方案一样,它看起来像是迭代的,但实际上不是。不是这样的原因是
    iter
    函数的框架看起来像

  • 如果废话
    • 在列表的第一个元素上递归
    • 否则就做点别的
  • 如果其他的废话
    • 迭代列表的其余部分
  • 关键的是,(1)和(2)总是发生,所以对列表中的car的调用不是尾部调用:它是一个完全成熟的递归调用

    也就是说,你可以做得更好:做这类事情的绝对标准方法是使用累加器:

    (define (fringe l)
      (define (fringe-loop thing accum)
        (cond
          ((null? thing)
           ;; we're at the end of the list or an element which is empty list
           accum)
          ((pair? thing)
           ;; we need to look at both the first of the list and the rest of the list
           ;; Note that the order is rest then first which means the accumulator
           ;; comes back in a good order
           (fringe-loop (car thing)
                        (fringe-loop (cdr thing) accum)))
          (else
           ;; not a list at all: collect this "atomic" thing
           (cons thing accum))))
      (fringe-loop l '()))
    
    请注意,这将自下而上构建边缘(线性)列表,这是使用递归构建线性列表的自然方式。为了实现这一点,它稍微偏离了它看待事物的方式,以便结果以正确的顺序出现。还要注意,这也不是迭代的:它是递归的,因为调用了
    (条纹循环…(条纹循环…)
    。但这一次要清楚得多

    它不是迭代的原因是搜索(树状,Lisp)列表的过程不是迭代的:这是SICP所称的“递归过程”,因为(Lisp树状)列表在其第一个字段和rest字段中都是递归定义的。您所能做的任何事情都不会使过程重复

    但是,通过显式地管理堆栈,从而将其转换为尾部递归版本,您可以使代码在实现级别显示为迭代的。尽管如此,计算过程的性质并未改变:

    (define (fringe l)
      (define (fringe-loop thing accum stack)
        (cond
          ((null? thing)
           ;; ignore the () sentinel or () element
           (if (null? stack)
               ;; nothing more to do
               accum
               ;; continue with the thing most recently put aside
               (fringe-loop (car stack) accum (cdr stack))))
          ((pair? thing)
           ;; carry on to the right, remembering to look to the left later
           (fringe-loop (cdr thing) accum (cons (car thing) stack)))
          (else
           ;; we're going to collect this atomic thing but we also need 
           ;; to check the stack
           (if (null? stack)
               ;; we're done
               (cons thing accum)
               ;; collect this and continue with what was put aside
               (fringe-loop (car stack) (cons thing accum) (cdr stack))))))
      (fringe-loop l '() '()))
    
    这是否值得,取决于您认为递归调用有多昂贵,以及是否存在任何递归限制。然而,显式管理下一步操作的一般技巧通常是有用的,因为它可以使控制搜索顺序更加容易


    (当然,请注意,对于任何程序,您都可以执行类似的操作!)

    存在三种情况的原因是,您正在导入一些标量/向量与其他语言的区别:Scheme没有标量/向量,也没有帮助。相反,列表是一个递归定义的对象:列表要么是空列表,要么是一对某物和一个列表。这意味着有两个区别,而不是一个:一个对象是一对,一个对象是空列表:

    (define (lyst? o)
      (or (null? o)
          (and (pair? o) (lyst? (cdr o)))))
    
    这与向量/标量的区别完全不同。我不知道你是从哪种语言得到这个结果的,但是想想这个数学是如何工作的:向量是在某个标量场上定义的,没有向量也是标量的。但是对于列表,有一个列表不是成对的。不要再考虑向量和标量了:考虑列表、对和空列表是没有帮助的

    迭代版本太可怕了,无法想象:SICP没有引入
    set是有原因的还没有

    首先,它实际上不是迭代的:就像网络上解决这个问题的大多数“迭代”解决方案一样,它看起来像是迭代的,但实际上不是。不是这样的原因是
    iter
    函数的框架看起来像

  • 如果废话
    • 在列表的第一个元素上递归
    • 否则就做点别的
  • 如果其他的废话
    • 迭代列表的其余部分
  • 关键的是,(1)和(2)总是发生,所以对列表中的car的调用不是尾部调用:它是一个完全成熟的递归调用

    也就是说你可以让这个m
    (define (fringe l)
      (define (fringe-loop thing accum stack)
        (cond
          ((null? thing)
           ;; ignore the () sentinel or () element
           (if (null? stack)
               ;; nothing more to do
               accum
               ;; continue with the thing most recently put aside
               (fringe-loop (car stack) accum (cdr stack))))
          ((pair? thing)
           ;; carry on to the right, remembering to look to the left later
           (fringe-loop (cdr thing) accum (cons (car thing) stack)))
          (else
           ;; we're going to collect this atomic thing but we also need 
           ;; to check the stack
           (if (null? stack)
               ;; we're done
               (cons thing accum)
               ;; collect this and continue with what was put aside
               (fringe-loop (car stack) (cons thing accum) (cdr stack))))))
      (fringe-loop l '() '()))
    
    (define (list? ls)
      (or (null? ls)
          (and (pair? ls)
               (list? (cdr ls)))))
    
    (define (tree? ls)
      (or (null? ls)
          (and (pair? ls)
               (tree? (car ls))
               (tree? (cdr ls)))))
    
    (define (tree? ls)
      (or (null? ls)
          (not (pair? ls))   ;; (atom? ls) is what we mean
          (and ;; (pair? ls)
               (tree? (car ls))
               (tree? (cdr ls)))))
    
    (define (tree-fringe ls)
    
      (cond 
          ((null? ls)
    
                       ls)
          ((not (pair? ls))   ;; (atom? ls) is what we mean
               (handle-atom-case ls))
    
          (else
               ;; (tree? (car ls))
               ;; (tree? (cdr ls))
    
              (let ((a (tree-fringe (car ls)))
                    (b (tree-fringe (cdr ls)))
    
                (append   a   b  )))))
    
    (define (handle-atom-case ls)  
          ;; bad name, inline its code inside 
          ;; the `tree-fringe` later, when we have it
    
        ;;  tree:       1         2
        ;; fringe:    ( 1 )     ( 2 )
    
          ........ )