【问题标题】:Curry-Skip Scheme跳过咖喱计划
【发布时间】:2014-02-19 04:32:14
【问题描述】:

我必须编写一个名为 curry-skip 的方案函数,它递归地返回列表的第 n 个元素,这样

(((curry-skip 1) 'foo) 'bar) => bar

我似乎无法弄清楚如何递归地执行此操作。我对Scheme还是很陌生,所以任何帮助都将不胜感激! 谢谢

【问题讨论】:

    标签: list scheme


    【解决方案1】:
    (define (curry-skip n)
      (if (zero? n)
          (lambda (x) x)
          (let ((rest (curry-skip (- n 1))))
            (lambda (x) rest))))
    

    您想计算返回的lambda 之外的(- n 1) 案例外部!因此,在上面,当n 为零时,只需返回一个参数 lambda;但是,当n 为正时,计算(- n 1) 的情况并返回忽略其参数并仅返回rest 的lambda。

    请注意,虽然不是您的问题的一部分,但与评论相关,但如果您需要真正的咖喱,就像在 ML 中一样,您需要语法帮助:

    (define-syntax curry*
      (syntax-rules ()
        ((_ (a) body ...) (lambda (a) body ...))
        ((_ (a b ...) body ...)
         (lambda (a) (curry* (b ...) body ...)))))
    

    注意递归如何为每个a, b, … 创建词法环境,以便可以正确评估bodycurry-skip 没有这种需求。

    这里有一些代码解决了关于在返回的 lambda 中执行“curry-skip”的注释。在lambda 之外进行递归只会在创建一个闭包时发生;在 lambda 内执行此操作会在每次调用时创建闭包。

    (define (curry-skip-o n)
      (if (zero? n)
          (lambda (x) x)
          (let ((rest (curry-skip-o (- n 1))))
            (lambda (x) rest))))
    
    (define (curry-skip-i n)
      (if (zero? n)
          (lambda (x) x)
          (lambda (x) (curry-skip-i (- n 1)))))
    
    (define (run skipper r n)
      (let ((f (skipper n)))
        ;; Repeat 'r' times with 'f'
        (let rep ((r r))
          (unless (zero? r)
            ;; Exhaust f
            (let rep ((i 0) (f f))
              (if (< i n)
                  (rep (+ i 1) (f i))
                  (f 0)))
            (rep (- r 1))))))
    
    > (time (run curry-skip-o 10000 10000))            ;; create ~10^4 closures
    running stats for (run curry-skip-o 10000 10000):
        no collections
        376 ms elapsed cpu time, including 0 ms collecting
        376 ms elapsed real time, including 0 ms collecting
        160032 bytes allocated
    > (time (run curry-skip-i 10000 10000))            ;; create ~10^8 closures
    running stats for (run curry-skip-i 10000 10000):
        191 collections
        1587 ms elapsed cpu time, including 965 ms collecting
        1588 ms elapsed real time, including 966 ms collecting
        1599840048 bytes allocated
    

    【讨论】:

    • 您的定义与另一个定义形成了很好的对比,但被否决了,因为强调的计算外部 n-1 案例的建议完全是虚假的。
    • 在返回的 lambda 之外进行递归可以避免每次调用 lambda 时执行其余的跳过。 (大概它被调用的次数比它创建的次数多得多。)如果它是一个真正的“咖喱”,那么它需要是 a) 句法和 b) 词法递归。我用真正的咖喱更新了答案。 (PS。感谢您解释否决票。)
    • 不,这仍然是错误的。假设一个合理的编译器,两者都会产生非常相似的代码和相同的速度。唯一微小的区别是在函数中进行减法或减法,但无论如何,即使这样也可以被一个好的编译器提升。我不知道您使用了哪种实现,但由于分配量太大,它很可能无法编译。 (Petite?——原则上它的免费版本不编译。)我使用的是 Racket 5.3.6,你可以看到外部版本稍慢:tmp.barzilay.org/sc.rkt
    • Ikarus 计划。很抱歉,您认为这两种方法会产生“相似代码”这一事实表明您对这两个选项缺乏了解。使用最多为“3”的“跳过”参数意味着算法的闭包创建在噪音中丢失。当然,如果问题的规模这么小,那谁在乎 - 使用你想要的算法。
    • OTOH,OP 很可能会说这是一些功课,因此不考虑优化 - 而 正是您的建议是虚假的原因:它促进了微- 优化您应该考虑整体问题并尝试获得最简单的代码。我喜欢您的回答,因为它演示了将闭包作为值进行操作,但是对于外部版本,即使这样也很容易丢失。这就是为什么我坚决支持 bogos-advice 主张的原因。
    【解决方案2】:

    我认为你这里有 3 个案例:

    1. n>0 - 向下递归直到 n=0
    2. n=0 - 你记住参数,因为这是你需要返回的最终值;记忆是通过闭包完成的
    3. else - 在完成之前忽略参数,然后最终返回记忆值

    所以

    (define (res val)                                ; [procedure used for case 2 - n=0]
      (define (res0 . x) (if (null? x) val res0))    ; [case 3]
      res0)
    
    (define (curry-skip n) 
      (cond
        ((< n 0) (error "n is negative"))
        ((= n 0) res)                                ; [case 2 - n = 0]
        (else    (lambda x (curry-skip (- n 1))))))  ; [case 1 - n > 0]
    

    然后

    -> (((curry-skip 0) 'foo))
    'foo
    -> ((((curry-skip 0) 'foo) 'bar))
    'foo
    -> ((((curry-skip 1) 'foo) 'bar))
    'bar
    -> (((((curry-skip 0) 'foo) 'bar) 'baz))
    'foo
    -> (((((curry-skip 1) 'foo) 'bar) 'baz))
    'bar
    -> (((((curry-skip 2) 'foo) 'bar) 'baz))
    'baz
    

    与您的问题相比,我的解决方案多了一对括号,但这是我所能得到的。

    【讨论】:

      【解决方案3】:

      看了你的例子后,我写了以下内容。这似乎相当简单,因此我无法解释它的原因。

      (define (curry-skip n) 
          (lambda (v) (if (= n 0) 
                          v 
                          (curry-skip (- n 1)))))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-02
        • 1970-01-01
        • 2018-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多