首页 文章

在球拍中通过循环追加的正确方法是什么?

提问于
浏览
3

今天开始学习球拍 . 我试图找到通过循环追加的正确方法,但无法找到答案或自己弄清楚语法 .

例如,如果我想使用hc-append一行九个圆圈,如何在不手动输入九个嵌套的hc-append过程的情况下执行此操作?

4 回答

  • 8

    你需要意识到的第一件事是,在Racket中“循环”实际上只是递归 . 在这种情况下,您希望将一堆绘图调用链接在一起 . 如果我们写出来,我们的目标是:

    (hc-append (circle 10) 
        (hc-append (circle 10)
            (hc-append (circle 10)
                (hc-append (circle 10)
                    (hc-append (circle 10)
                        (hc-append (circle 10)
                            (hc-append (circle 10)
                                (hc-append (circle 10)
                                    (hc-append (circle 10))))))))))
    

    我假设我们所有的圈子都是相同的半径 .

    现在,因为我们're going to be writing a recursive method, we need to think of our base case. We want exactly nine circles drawn. Let'调用这个最大圈数 max . 我们的基本情况,当我们突破"loop"时,将是 max 迭代,或 (= iterations max) .

    现在为递归本身 . 我们已经知道我们需要传递至少两个变量,当前迭代 iterations 和最大迭代 max . 如果你看一下上面的代码,你会发现所有"loops"中的重复元素是 (circle 10) . 现在有很多方法可以传递它 - 有些人会选择仅仅通过半径 - 但我认为最简单的方法是传递圆圈的图形 .

    最后,我们还要传递到目前为止我们所做的图片 . 也就是说,当我们向我们的链附加一个圆圈时,我们需要将它传递回递归方法,以便我们可以继续追加 .

    现在我们已经得到了平方,我们可以定义递归方法的结构,我们称之为 circle-chain-recursive

    (define (circle-chain-recursive iteration max crcle output)
        ; body here
    )
    

    我们方法的"guts"将是 if . 如果我们已达到最大迭代次数,则返回输出 . 否则追加另一个圆圈,递增 iteration ,然后再次调用该方法 .

    (define (circle-chain-recursive iteration max crcle output)
      (if (= iteration max) 
          output
          (circle-chain-recursive
            (+ 1 iteration) max crcle (hc-append crcle output))))
    

    我个人不喜欢直接调用这样的递归循环方法,所以我会编写一个这样的辅助方法:

    (define (circle-chain num radius)
      (circle-chain-recursive 0 num (circle radius) (circle 0)))
    

    现在,如果我想要一个半径为10的9个圆的系列,我所要做的就是调用 (circle-chain 9 10) .

    你会注意到我传递了 (circle 0) 作为名为 output 的参数 . 这是因为 hc-append 方法需要 pict 参数 . 既然我们对 slideshow/pict 库不太熟悉就知道了 .

    我希望稍微澄清一下 .

  • 0

    Racket中有三种循环样式:

    #1 for循环风格

    此样式使用“球拍指南”第11章中描述的语法"Iterations and Comprehensions" . 它看起来像这样:

    (require slideshow/pict)
    
    (for/fold ([result (blank)]) ([i (in-range 9)])
      (hc-append (circle 10) result))
    

    在这种风格中,在for之后的第一个括号中定义了循环的临时变量 . 在我的例子中有一个这样的变量叫做result . 然后定义迭代变量以及它们迭代的内容 . 所以在这里,我循环遍历0到8的数字 . 循环体对每个i运行一次,每次将结果分配给结果,循环的最终值是结尾处的结果值 .

    #2 Map 和折叠样式 .

    此样式在Section 3.8 of the Guide中描述 . 它看起来像这样:

    (require slideshow/pict)
    (foldl hc-append (blank) (build-list 9 (lambda (i) (circle 10))))
    

    此代码首先列出9个圆圈:

    (define o (circle 10))
    (build-list 9 (lambda (i) o)  --->  (list o o o o o o o o o)
    

    列表符号是更详细的符号表示的简写 .

    (list o o o o o o o o o)  --->  (cons o (cons o (cons o (cons o (cons o (cons o (cons o (cons o (cons o empty)))))))))
    

    考虑foldl函数的方法是将一段数据转换为一个计算 . 它将列表作为输入,并将其转换为函数调用的集合 .

    foldl函数获取列表并用第一个参数替换列表中的每个cons,并用第二个参数替换列表末尾的空 . 在示例中,我传递了hc-append函数和(空白),因此替换如下所示:

    (foldl hc-append (blank) ...) --->
    (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (hc-append o (blank))))))))))
    

    这个函数调用序列正是您希望避免手动编写的函数 . Foldl为你计算它 .

    #3递归样式

    这是罗迪在他的回答中描述的风格 . 作为编码风格的一般问题,如果没有简单的* for,map或fold可能,则应该只使用递归样式,例如遍历递归数据结构时 .

  • 4

    冷冻豌 beans 的罗迪有这个答案几乎涵盖 .

    我想补充一点,因为这类事情是如此常见 - 重复一个过程并积累结果 - 在Racket语言中有一些功能可以简洁地表达这个想法 . 其中一个功能是Racket的for/fold循环 . 举个例子:

    > (for/fold ([result 'Go!]) 
                ([i (in-range 5)])
        (list 'Duck result))
    '(Duck (Duck (Duck (Duck (Duck Go!)))))
    

    在这里,我们重复操作(列出'Duck结果)初始值'Go!积累结果 . for / fold 's implementation is based on the recursive process that Roddy describes, so once you have the fundamentals down, you'我知道足够用于/折叠舒适 .

  • 3

    这是一个老帖子,但它是高度阅读,因此我添加一个答案 . 使用'named let'的方法也非常方便:

    (define (f)
      (let loop ((counter 1)
                 (result (blank)))
        (if (< counter 10)
            (loop (add1 counter)
                  (hc-append (circle 10) result))
            result)))
    

相关问题