你如何在方案中实现一个简单的列表迭代器?

Posted

技术标签:

【中文标题】你如何在方案中实现一个简单的列表迭代器?【英文标题】:How do you implement a simple list iterator in scheme? 【发布时间】:2021-12-01 12:04:49 【问题描述】:

我正在尝试仅使用基本函数(car、cdr、cons 等)比较 Scheme 中列表列表中的条目,但不知道如何迭代列表。我试着写这个:

(define (iterate L X)
    (cond ((null? L) '())
          ((> X 0) (iterate ((cdr L) (- X 1))))
          ))

但我不确定为什么它不起作用。我的想法是迭代只会返回 n cdr 之后的列表列表。例如,我试图得到:

(iterate ( ((10 20) (20 30) (30 40) (40 50)) '2)) -> ((30 40) (40 50))
(iterate ( ((10 20) (20 30) (30 40) (40 50)) '3)) -> ((40 50))

这是我得到的错误,不知道是什么意思:

*** ERROR: invalid application: (20 30)
Stack Trace:
_______________________________________
  0  (20 30)
        At line 45 of "./main.sc"
  1  ((10 20) (20 30) (30 40) (40 50))
        At line 45 of "./main.sc"
  2  (((10 20) (20 30) (30 40) (40 50)) '3)
        At line 45 of "./main.sc"
  3  (iterate (((10 20) (20 30) (30 40) (40 50)) '3))
        At line 45 of "./main.sc"

感谢任何帮助。谢谢!

【问题讨论】:

您正在尝试使用cdr 返回的列表,就像它是一个可以调用的函数一样。 【参考方案1】:

有两个基本问题:

    在 Scheme 中不要用括号括起函数参数;语法是(function arg1 arg2),而不是(function (arg1 arg2))。后者首先将过程arg1 应用于arg2,然后将其结果传递给function((10 20) (20 30) (30 40) (40 50)) 不会创建列表,它会尝试将过程(10 20) 应用于参数(20 30)(30 40)(40 50)。您的实现首先评估第一个参数 - (20 30) - 在 (10 20) 之前,这会尝试将 20 作为过程应用到参数 30。这是“无效申请”。 (如果首先评估该过程,您会看到“无效的应用程序:(10 20)”。)

您需要引用您的列表参数或使用list 过程。

解决这两个问题

(define (iterate L X)
    (cond ((null? L) '())
          ((> X 0) (iterate (cdr L) (- X 1)))))

(iterate '((10 20) (20 30) (30 40) (40 50)) '3)

现在你只需要修复X在列表为空之前达到零的问题...

【讨论】:

【参考方案2】:

基本上,Scheme 中的所有控制流,甚至是报告中的控制流,都是递归过程的语法糖。例如。当你用 JS 写这个的时候:

const arr = [];
for (let e of lst) 
  arr.push(e*2);

return arr;

在Scheme中你会这样写:

(define (loop lst acc)
  (if (null? lst)
      (reverse acc)
      (loop (cdr lst) (cons (* (car lst) 2) acc))))
(loop lst '())

我们甚至还有一个语法糖可以更清楚地做到这一点:

(let loop ((lst lst) (acc '()))
  (if (null? lst)
      (reverse acc)
      (loop (cdr lst) (cons (* (car lst) 2) acc))))

在 JS 中,我们有 Array.forEachArray.reduceArray.map。在 Scheme 中,我们有 for-eachfold-leftfold-rightmap。例如。上面的代码在JS中可以这样写:

[1, 2, 3, 4].map(v => v*2); // ==> [2, 3, 6, 8]

在Scheme中像这样:

(map (lambda (v) (* v 2)) lst) ; ==> (2 3 6 8)

所以我猜你不能使用这些列表过程,但你可以自己创建它,或者像我的第一个例子一样滚动你自己的递归。

【讨论】:

以上是关于你如何在方案中实现一个简单的列表迭代器?的主要内容,如果未能解决你的问题,请参考以下文章

设计模式这样玩泰简单(Golang版)-迭代器模式

设计模式这样玩泰简单(Golang版)-迭代器模式

如何在OpenGL ES着色器中实现Catmull Clark细分算法

Python 迭代器

如何在 HTML5 中实现一个简单的音频播放列表

如何在基类中实现子类迭代器的统一接口?