争夺功能如何工作? (经验丰富的Schemer的第1章)

发布于 2025-02-08 16:14:39 字数 861 浏览 17 评论 0原文

根据这本书,这是函数定义的内容,

功能争夺的元组不超过其自身索引,并返回相同长度的元组。参数中的每个数字都被视为从其自身位置到元组早期的向后索引。每个位置的结果是通过根据此索引从当前位置向后计数来获得的。

这些是一些示例,

; Examples of scramble
(scramble '(1 1 1 3 4 2 1 1 9 2))       ; '(1 1 1 1 1 4 1 1 1 9)
(scramble '(1 2 3 4 5 6 7 8 9))         ; '(1 1 1 1 1 1 1 1 1)
(scramble '(1 2 3 1 2 3 4 1 8 2 10))    ; '(1 1 1 1 1 1 1 1 2 8 2)

这是实现,

(define pick
  (λ (i lat)
    (cond
      ((eq? i 1) (car lat))
      (else (pick (sub1 i)
                  (cdr lat))))))

(define scramble-b
  (lambda (tup rev-pre)
    (cond
      ((null? tup) '())
      (else
       (cons (pick (car tup) (cons (car tup) rev-pre))
             (scramble-b (cdr tup)
                         (cons (car tup) rev-pre)))))))

(define scramble
  (lambda (tup)
    (scramble-b tup '())))

According to the book, this is what the function definition is,

The function scramble takes a non-empty tuple in which no argument is greater than its own index and returns a tuple of same length. Each number in the argument is treated as a backward index from its own position to a point earlier in tuple. The result at each position is obtained by counting backward from the current position according to this index.

And these are some examples,

; Examples of scramble
(scramble '(1 1 1 3 4 2 1 1 9 2))       ; '(1 1 1 1 1 4 1 1 1 9)
(scramble '(1 2 3 4 5 6 7 8 9))         ; '(1 1 1 1 1 1 1 1 1)
(scramble '(1 2 3 1 2 3 4 1 8 2 10))    ; '(1 1 1 1 1 1 1 1 2 8 2)

Here is the implementation,

(define pick
  (λ (i lat)
    (cond
      ((eq? i 1) (car lat))
      (else (pick (sub1 i)
                  (cdr lat))))))

(define scramble-b
  (lambda (tup rev-pre)
    (cond
      ((null? tup) '())
      (else
       (cons (pick (car tup) (cons (car tup) rev-pre))
             (scramble-b (cdr tup)
                         (cons (car tup) rev-pre)))))))

(define scramble
  (lambda (tup)
    (scramble-b tup '())))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

丘比特射中我 2025-02-15 16:14:39

在这种情况下,使用非常最小的语言版本意味着代码足够详细,以至于理解算法并不容易。

解决这个问题的一种方法是用更丰富的语言编写程序,然后确定现在很明显的算法如何在最低版本中实现。让我们选择球拍作为丰富的语言。

球拍具有一个函数(和方案),称为list-ref(list-ref li)返回i 的TH元素l,基于零。

它也有一个很好的“序列”概念,这些序列几乎是“您可以迭代的东西”和一堆构造,其名称以为 for 开始,以迭代序列。有两个功能使我们关心的序列:

  • in-Naturals是自然数的无限序列,默认情况下是从0开始的,但是(在-naturals n)n开始。
  • in-list从列表中制作一个序列(列表实际上已经是一个序列,但是in-list使事情变得更清晰,并且谣言也更快)。

我们关心的迭代构造是/列表,它通过某些序列进行迭代,并将其从身体收集到列表中。

鉴于这些,那么算法几乎是微不足道的:我们想沿列表迭代,跟踪当前索引,然后进行适当的扣除以在列表中更进一步。唯一的非平凡位是处理零与一个基于索引的零。

(define (scramble l)
  (for/list ([index (in-naturals)]
             [element (in-list l)])
    (list-ref l (+ (- index element) 1))))

实际上,如果我们导致naturals1

(define (scramble l)
  (for/list ([index (in-naturals 1)]
             (element (in-list l)))
    (list-ref l (- index element))))

计数,该算法非常清楚,您可以检查它在书中给出答案:

> (scramble '(1 1 1 3 4 2 1 1 9 2))
'(1 1 1 1 1 4 1 1 1 9)

现在仍然可以阐明书中代码如何实现相同的算法。那是一个贴心的,但是一旦您知道算法是什么,它应该很简单。

This is a case where using a very minimal version of the language means that the code is verbose enough that understanding the algorithm is not perhaps easy.

One way of dealing with this problem is to write the program in a much richer language, and then work out how the algorithm, which is now obvious, is implemented in the minimal version. Let's pick Racket as the rich language.

Racket has a function (as does Scheme) called list-ref: (list-ref l i) returns the ith element of l, zero-based.

It also has a nice notion of 'sequences' which are pretty much 'things you can iterate over' and a bunch of constructs whose names begin with for for iterating over sequences. There are two functions which make sequences we care about:

  • in-naturals makes an infinite sequence of the natural numbers, which by default starts from 0, but (in-naturals n) starts from n.
  • in-list makes a sequence from a list (a list is already a sequence in fact, but in-list makes things clearer and there are rumours also faster).

And the iteration construct we care about is for/list which iterates over some sequences and collects the result from its body into a list.

Given these, then the algorithm is almost trivial: we want to iterate along the list, keeping track of the current index and then do the appropriate subtraction to pick a value further back along the list. The only non-trivial bit is dealing with zero- vs one-based indexing.

(define (scramble l)
  (for/list ([index (in-naturals)]
             [element (in-list l)])
    (list-ref l (+ (- index element) 1))))

And in fact if we cause in-naturals to count from 1 we can avoid the awkward adding-1:

(define (scramble l)
  (for/list ([index (in-naturals 1)]
             (element (in-list l)))
    (list-ref l (- index element))))

Now looking at this code, even if you don't know Racket, the algorithm is very clear, and you can check it gives the answers in the book:

> (scramble '(1 1 1 3 4 2 1 1 9 2))
'(1 1 1 1 1 4 1 1 1 9)

Now it remains to work out how the code in the book implements the same algorithm. That's fiddly, but once you know what the algorithm is it should be straightforward.

雄赳赳气昂昂 2025-02-15 16:14:39

如果口头描述看起来模糊且难以遵循,我们可以尝试遵循代码本身,将其变成更为视觉的伪代码,

pick i [x, ...ys] = 
  case i { 
     1 --> x ;
     pick (i-1) ys }
==>
  pick i xs = nth1 i xs
     (* 1 <= i <= |xs| *)

scramble xs = 
   scramble2 xs []

scramble2 xs revPre =
 case xs {
   [] --> [] ;
   [x, ...ys] -->
     [ pick x [x, ...revPre],
       ...scramble2 ys
              [x, ...revPre]] }

因此,

scramble [x,y,z,w, ...] 
 =
  [ nth1 x [x]       (*x=1..1*)
  , nth1 y [y,x]     (*y=1..2*)
  , nth1 z [z,y,x]   (*z=1..3*)
  , nth1 w [w,z,y,x] (*w=1..4*)
  , ... ]

因此,输入列表中的每个元素都用作索引中的索引中的索引中的相反前缀。列表,直到和包括该元素。换句话说,在计数 backwards 时,一个索引从元素到左,IE,to list的 start

因此,我们现在已经可视化了代码在做什么,并且还发现了其输入列表元素的要求。

If the verbal description looks vague and hard to follow, we can try following the code itself, turning it into a more visual pseudocode as we go:

pick i [x, ...ys] = 
  case i { 
     1 --> x ;
     pick (i-1) ys }
==>
  pick i xs = nth1 i xs
     (* 1 <= i <= |xs| *)

scramble xs = 
   scramble2 xs []

scramble2 xs revPre =
 case xs {
   [] --> [] ;
   [x, ...ys] -->
     [ pick x [x, ...revPre],
       ...scramble2 ys
              [x, ...revPre]] }

Thus,

scramble [x,y,z,w, ...] 
 =
  [ nth1 x [x]       (*x=1..1*)
  , nth1 y [y,x]     (*y=1..2*)
  , nth1 z [z,y,x]   (*z=1..3*)
  , nth1 w [w,z,y,x] (*w=1..4*)
  , ... ]

Thus each element in the input list is used as an index into the reversed prefix of that list, up to and including that element. In other words, an index into the prefix while counting backwards, i.e. from the element to the left, i.e. towards the list's start.

So we have now visualized what the code is doing, and have also discovered requirements for its input list's elements.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文